tutus-chain/pkg/core/state/collocatio.go

1437 lines
47 KiB
Go

package state
import (
"errors"
"fmt"
"math/big"
"github.com/tutus-one/tutus-chain/pkg/io"
"github.com/tutus-one/tutus-chain/pkg/util"
"github.com/tutus-one/tutus-chain/pkg/vm/stackitem"
)
// OpportunityType represents the type of investment opportunity.
type OpportunityType uint8
// Investment opportunity types.
const (
OpportunityPIO OpportunityType = 0 // Public Investment Opportunity - universal access
OpportunityEIO OpportunityType = 1 // Employee Investment Opportunity - workplace
OpportunityCIO OpportunityType = 2 // Contractor Investment Opportunity - gig economy
)
// OpportunityStatus represents the status of an investment opportunity.
type OpportunityStatus uint8
// Investment opportunity statuses.
const (
OpportunityDraft OpportunityStatus = 0 // Being created
OpportunityVoting OpportunityStatus = 1 // Under democratic voting
OpportunityActive OpportunityStatus = 2 // Open for investments
OpportunityClosed OpportunityStatus = 3 // No longer accepting investments
OpportunityCompleted OpportunityStatus = 4 // Returns distributed
OpportunityCancelled OpportunityStatus = 5 // Cancelled with refunds
OpportunityFailed OpportunityStatus = 6 // Did not meet minimum requirements
)
// InvestmentStatus represents the status of an individual investment.
type InvestmentStatus uint8
// Investment statuses.
const (
InvestmentActive InvestmentStatus = 0 // Investment is active
InvestmentWithdrawn InvestmentStatus = 1 // Investor withdrew
InvestmentCompleted InvestmentStatus = 2 // Returns distributed
InvestmentRefunded InvestmentStatus = 3 // Refunded due to cancellation
)
// EligibilityType represents eligibility flags.
type EligibilityType uint8
// Eligibility flags (can be combined).
const (
EligibilityNone EligibilityType = 0
EligibilityPIO EligibilityType = 1 << 0 // Public investment eligible
EligibilityEIO EligibilityType = 1 << 1 // Employee investment eligible
EligibilityCIO EligibilityType = 1 << 2 // Contractor investment eligible
EligibilityAll EligibilityType = EligibilityPIO | EligibilityEIO | EligibilityCIO
)
// InvestmentOpportunity represents an investment opportunity.
type InvestmentOpportunity struct {
ID uint64 // Unique opportunity identifier
Type OpportunityType // PIO, EIO, or CIO
Status OpportunityStatus // Current status
Creator util.Uint160 // Creator's address (must have Vita)
SponsorVitaID uint64 // Sponsor's Vita ID (for EIO: employer, CIO: platform)
Name string // Opportunity name
Description string // Investment description
TermsHash util.Uint256 // Hash of full terms (off-chain)
MinParticipants uint64 // Required minimum participants
MaxParticipants uint64 // Maximum allowed participants (0 = unlimited)
CurrentParticipants uint64 // Current number of participants
MinInvestment uint64 // Minimum VTS investment per person
MaxInvestment uint64 // Maximum VTS investment per person
TotalPool uint64 // Total VTS in opportunity
TargetPool uint64 // Target total pool size
ExpectedReturns uint64 // Projected returns (basis points, 10000 = 100%)
RiskLevel uint8 // Risk level 1-10 (AI-assessed)
VotingDeadline uint32 // Block height for voting deadline
InvestmentDeadline uint32 // Block height for investment deadline
MaturityDate uint32 // Block height when returns distributed
ProposalID uint64 // Associated Eligere proposal ID (0 = none)
CreatedAt uint32 // Block height when created
UpdatedAt uint32 // Block height when last updated
}
// DecodeBinary implements the io.Serializable interface.
func (o *InvestmentOpportunity) DecodeBinary(br *io.BinReader) {
o.ID = br.ReadU64LE()
o.Type = OpportunityType(br.ReadB())
o.Status = OpportunityStatus(br.ReadB())
br.ReadBytes(o.Creator[:])
o.SponsorVitaID = br.ReadU64LE()
o.Name = br.ReadString()
o.Description = br.ReadString()
br.ReadBytes(o.TermsHash[:])
o.MinParticipants = br.ReadU64LE()
o.MaxParticipants = br.ReadU64LE()
o.CurrentParticipants = br.ReadU64LE()
o.MinInvestment = br.ReadU64LE()
o.MaxInvestment = br.ReadU64LE()
o.TotalPool = br.ReadU64LE()
o.TargetPool = br.ReadU64LE()
o.ExpectedReturns = br.ReadU64LE()
o.RiskLevel = br.ReadB()
o.VotingDeadline = br.ReadU32LE()
o.InvestmentDeadline = br.ReadU32LE()
o.MaturityDate = br.ReadU32LE()
o.ProposalID = br.ReadU64LE()
o.CreatedAt = br.ReadU32LE()
o.UpdatedAt = br.ReadU32LE()
}
// EncodeBinary implements the io.Serializable interface.
func (o *InvestmentOpportunity) EncodeBinary(bw *io.BinWriter) {
bw.WriteU64LE(o.ID)
bw.WriteB(byte(o.Type))
bw.WriteB(byte(o.Status))
bw.WriteBytes(o.Creator[:])
bw.WriteU64LE(o.SponsorVitaID)
bw.WriteString(o.Name)
bw.WriteString(o.Description)
bw.WriteBytes(o.TermsHash[:])
bw.WriteU64LE(o.MinParticipants)
bw.WriteU64LE(o.MaxParticipants)
bw.WriteU64LE(o.CurrentParticipants)
bw.WriteU64LE(o.MinInvestment)
bw.WriteU64LE(o.MaxInvestment)
bw.WriteU64LE(o.TotalPool)
bw.WriteU64LE(o.TargetPool)
bw.WriteU64LE(o.ExpectedReturns)
bw.WriteB(o.RiskLevel)
bw.WriteU32LE(o.VotingDeadline)
bw.WriteU32LE(o.InvestmentDeadline)
bw.WriteU32LE(o.MaturityDate)
bw.WriteU64LE(o.ProposalID)
bw.WriteU32LE(o.CreatedAt)
bw.WriteU32LE(o.UpdatedAt)
}
// ToStackItem implements stackitem.Convertible interface.
func (o *InvestmentOpportunity) ToStackItem() (stackitem.Item, error) {
return stackitem.NewStruct([]stackitem.Item{
stackitem.NewBigInteger(big.NewInt(int64(o.ID))),
stackitem.NewBigInteger(big.NewInt(int64(o.Type))),
stackitem.NewBigInteger(big.NewInt(int64(o.Status))),
stackitem.NewByteArray(o.Creator.BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(o.SponsorVitaID))),
stackitem.NewByteArray([]byte(o.Name)),
stackitem.NewByteArray([]byte(o.Description)),
stackitem.NewByteArray(o.TermsHash.BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(o.MinParticipants))),
stackitem.NewBigInteger(big.NewInt(int64(o.MaxParticipants))),
stackitem.NewBigInteger(big.NewInt(int64(o.CurrentParticipants))),
stackitem.NewBigInteger(big.NewInt(int64(o.MinInvestment))),
stackitem.NewBigInteger(big.NewInt(int64(o.MaxInvestment))),
stackitem.NewBigInteger(big.NewInt(int64(o.TotalPool))),
stackitem.NewBigInteger(big.NewInt(int64(o.TargetPool))),
stackitem.NewBigInteger(big.NewInt(int64(o.ExpectedReturns))),
stackitem.NewBigInteger(big.NewInt(int64(o.RiskLevel))),
stackitem.NewBigInteger(big.NewInt(int64(o.VotingDeadline))),
stackitem.NewBigInteger(big.NewInt(int64(o.InvestmentDeadline))),
stackitem.NewBigInteger(big.NewInt(int64(o.MaturityDate))),
stackitem.NewBigInteger(big.NewInt(int64(o.ProposalID))),
stackitem.NewBigInteger(big.NewInt(int64(o.CreatedAt))),
stackitem.NewBigInteger(big.NewInt(int64(o.UpdatedAt))),
}), nil
}
// FromStackItem implements stackitem.Convertible interface.
func (o *InvestmentOpportunity) FromStackItem(item stackitem.Item) error {
items, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not a struct")
}
if len(items) != 23 {
return fmt.Errorf("wrong number of elements: expected 23, got %d", len(items))
}
id, err := items[0].TryInteger()
if err != nil {
return fmt.Errorf("invalid id: %w", err)
}
o.ID = id.Uint64()
oppType, err := items[1].TryInteger()
if err != nil {
return fmt.Errorf("invalid type: %w", err)
}
o.Type = OpportunityType(oppType.Uint64())
status, err := items[2].TryInteger()
if err != nil {
return fmt.Errorf("invalid status: %w", err)
}
o.Status = OpportunityStatus(status.Uint64())
creator, err := items[3].TryBytes()
if err != nil {
return fmt.Errorf("invalid creator: %w", err)
}
o.Creator, err = util.Uint160DecodeBytesBE(creator)
if err != nil {
return fmt.Errorf("invalid creator address: %w", err)
}
sponsorVitaID, err := items[4].TryInteger()
if err != nil {
return fmt.Errorf("invalid sponsorVitaID: %w", err)
}
o.SponsorVitaID = sponsorVitaID.Uint64()
name, err := items[5].TryBytes()
if err != nil {
return fmt.Errorf("invalid name: %w", err)
}
o.Name = string(name)
description, err := items[6].TryBytes()
if err != nil {
return fmt.Errorf("invalid description: %w", err)
}
o.Description = string(description)
termsHash, err := items[7].TryBytes()
if err != nil {
return fmt.Errorf("invalid termsHash: %w", err)
}
o.TermsHash, err = util.Uint256DecodeBytesBE(termsHash)
if err != nil {
return fmt.Errorf("invalid termsHash: %w", err)
}
minParticipants, err := items[8].TryInteger()
if err != nil {
return fmt.Errorf("invalid minParticipants: %w", err)
}
o.MinParticipants = minParticipants.Uint64()
maxParticipants, err := items[9].TryInteger()
if err != nil {
return fmt.Errorf("invalid maxParticipants: %w", err)
}
o.MaxParticipants = maxParticipants.Uint64()
currentParticipants, err := items[10].TryInteger()
if err != nil {
return fmt.Errorf("invalid currentParticipants: %w", err)
}
o.CurrentParticipants = currentParticipants.Uint64()
minInvestment, err := items[11].TryInteger()
if err != nil {
return fmt.Errorf("invalid minInvestment: %w", err)
}
o.MinInvestment = minInvestment.Uint64()
maxInvestment, err := items[12].TryInteger()
if err != nil {
return fmt.Errorf("invalid maxInvestment: %w", err)
}
o.MaxInvestment = maxInvestment.Uint64()
totalPool, err := items[13].TryInteger()
if err != nil {
return fmt.Errorf("invalid totalPool: %w", err)
}
o.TotalPool = totalPool.Uint64()
targetPool, err := items[14].TryInteger()
if err != nil {
return fmt.Errorf("invalid targetPool: %w", err)
}
o.TargetPool = targetPool.Uint64()
expectedReturns, err := items[15].TryInteger()
if err != nil {
return fmt.Errorf("invalid expectedReturns: %w", err)
}
o.ExpectedReturns = expectedReturns.Uint64()
riskLevel, err := items[16].TryInteger()
if err != nil {
return fmt.Errorf("invalid riskLevel: %w", err)
}
o.RiskLevel = uint8(riskLevel.Uint64())
votingDeadline, err := items[17].TryInteger()
if err != nil {
return fmt.Errorf("invalid votingDeadline: %w", err)
}
o.VotingDeadline = uint32(votingDeadline.Uint64())
investmentDeadline, err := items[18].TryInteger()
if err != nil {
return fmt.Errorf("invalid investmentDeadline: %w", err)
}
o.InvestmentDeadline = uint32(investmentDeadline.Uint64())
maturityDate, err := items[19].TryInteger()
if err != nil {
return fmt.Errorf("invalid maturityDate: %w", err)
}
o.MaturityDate = uint32(maturityDate.Uint64())
proposalID, err := items[20].TryInteger()
if err != nil {
return fmt.Errorf("invalid proposalID: %w", err)
}
o.ProposalID = proposalID.Uint64()
createdAt, err := items[21].TryInteger()
if err != nil {
return fmt.Errorf("invalid createdAt: %w", err)
}
o.CreatedAt = uint32(createdAt.Uint64())
updatedAt, err := items[22].TryInteger()
if err != nil {
return fmt.Errorf("invalid updatedAt: %w", err)
}
o.UpdatedAt = uint32(updatedAt.Uint64())
return nil
}
// Investment represents an individual's investment in an opportunity.
type Investment struct {
ID uint64 // Unique investment ID
OpportunityID uint64 // Opportunity invested in
VitaID uint64 // Investor's Vita ID
Investor util.Uint160 // Investor's address
Amount uint64 // VTS amount invested
Status InvestmentStatus // Current status
ReturnAmount uint64 // Returns received (when completed)
CreatedAt uint32 // Block height when invested
UpdatedAt uint32 // Block height when last updated
}
// DecodeBinary implements the io.Serializable interface.
func (i *Investment) DecodeBinary(br *io.BinReader) {
i.ID = br.ReadU64LE()
i.OpportunityID = br.ReadU64LE()
i.VitaID = br.ReadU64LE()
br.ReadBytes(i.Investor[:])
i.Amount = br.ReadU64LE()
i.Status = InvestmentStatus(br.ReadB())
i.ReturnAmount = br.ReadU64LE()
i.CreatedAt = br.ReadU32LE()
i.UpdatedAt = br.ReadU32LE()
}
// EncodeBinary implements the io.Serializable interface.
func (i *Investment) EncodeBinary(bw *io.BinWriter) {
bw.WriteU64LE(i.ID)
bw.WriteU64LE(i.OpportunityID)
bw.WriteU64LE(i.VitaID)
bw.WriteBytes(i.Investor[:])
bw.WriteU64LE(i.Amount)
bw.WriteB(byte(i.Status))
bw.WriteU64LE(i.ReturnAmount)
bw.WriteU32LE(i.CreatedAt)
bw.WriteU32LE(i.UpdatedAt)
}
// ToStackItem implements stackitem.Convertible interface.
func (i *Investment) ToStackItem() (stackitem.Item, error) {
return stackitem.NewStruct([]stackitem.Item{
stackitem.NewBigInteger(big.NewInt(int64(i.ID))),
stackitem.NewBigInteger(big.NewInt(int64(i.OpportunityID))),
stackitem.NewBigInteger(big.NewInt(int64(i.VitaID))),
stackitem.NewByteArray(i.Investor.BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(i.Amount))),
stackitem.NewBigInteger(big.NewInt(int64(i.Status))),
stackitem.NewBigInteger(big.NewInt(int64(i.ReturnAmount))),
stackitem.NewBigInteger(big.NewInt(int64(i.CreatedAt))),
stackitem.NewBigInteger(big.NewInt(int64(i.UpdatedAt))),
}), nil
}
// FromStackItem implements stackitem.Convertible interface.
func (i *Investment) FromStackItem(item stackitem.Item) error {
items, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not a struct")
}
if len(items) != 9 {
return fmt.Errorf("wrong number of elements: expected 9, got %d", len(items))
}
id, err := items[0].TryInteger()
if err != nil {
return fmt.Errorf("invalid id: %w", err)
}
i.ID = id.Uint64()
oppID, err := items[1].TryInteger()
if err != nil {
return fmt.Errorf("invalid opportunityID: %w", err)
}
i.OpportunityID = oppID.Uint64()
vitaID, err := items[2].TryInteger()
if err != nil {
return fmt.Errorf("invalid vitaID: %w", err)
}
i.VitaID = vitaID.Uint64()
investor, err := items[3].TryBytes()
if err != nil {
return fmt.Errorf("invalid investor: %w", err)
}
i.Investor, err = util.Uint160DecodeBytesBE(investor)
if err != nil {
return fmt.Errorf("invalid investor address: %w", err)
}
amount, err := items[4].TryInteger()
if err != nil {
return fmt.Errorf("invalid amount: %w", err)
}
i.Amount = amount.Uint64()
status, err := items[5].TryInteger()
if err != nil {
return fmt.Errorf("invalid status: %w", err)
}
i.Status = InvestmentStatus(status.Uint64())
returnAmount, err := items[6].TryInteger()
if err != nil {
return fmt.Errorf("invalid returnAmount: %w", err)
}
i.ReturnAmount = returnAmount.Uint64()
createdAt, err := items[7].TryInteger()
if err != nil {
return fmt.Errorf("invalid createdAt: %w", err)
}
i.CreatedAt = uint32(createdAt.Uint64())
updatedAt, err := items[8].TryInteger()
if err != nil {
return fmt.Errorf("invalid updatedAt: %w", err)
}
i.UpdatedAt = uint32(updatedAt.Uint64())
return nil
}
// InvestorEligibility represents an investor's eligibility status.
type InvestorEligibility struct {
VitaID uint64 // Investor's Vita ID
Investor util.Uint160 // Investor's address
Eligibility EligibilityType // Eligibility flags
ScireCompleted bool // Investment education completed
RiskScore uint8 // AI-calculated risk tolerance (1-10)
TotalInvested uint64 // Total VTS currently invested
TotalReturns uint64 // Total returns received
ActiveInvestments uint64 // Number of active investments
CompletedInvestments uint64 // Number of completed investments
HasViolations bool // Any past investment abuse
ViolationCount uint8 // Number of violations
LastActivity uint32 // Block height of last activity
CreatedAt uint32 // Block height when created
UpdatedAt uint32 // Block height when updated
}
// DecodeBinary implements the io.Serializable interface.
func (e *InvestorEligibility) DecodeBinary(br *io.BinReader) {
e.VitaID = br.ReadU64LE()
br.ReadBytes(e.Investor[:])
e.Eligibility = EligibilityType(br.ReadB())
e.ScireCompleted = br.ReadBool()
e.RiskScore = br.ReadB()
e.TotalInvested = br.ReadU64LE()
e.TotalReturns = br.ReadU64LE()
e.ActiveInvestments = br.ReadU64LE()
e.CompletedInvestments = br.ReadU64LE()
e.HasViolations = br.ReadBool()
e.ViolationCount = br.ReadB()
e.LastActivity = br.ReadU32LE()
e.CreatedAt = br.ReadU32LE()
e.UpdatedAt = br.ReadU32LE()
}
// EncodeBinary implements the io.Serializable interface.
func (e *InvestorEligibility) EncodeBinary(bw *io.BinWriter) {
bw.WriteU64LE(e.VitaID)
bw.WriteBytes(e.Investor[:])
bw.WriteB(byte(e.Eligibility))
bw.WriteBool(e.ScireCompleted)
bw.WriteB(e.RiskScore)
bw.WriteU64LE(e.TotalInvested)
bw.WriteU64LE(e.TotalReturns)
bw.WriteU64LE(e.ActiveInvestments)
bw.WriteU64LE(e.CompletedInvestments)
bw.WriteBool(e.HasViolations)
bw.WriteB(e.ViolationCount)
bw.WriteU32LE(e.LastActivity)
bw.WriteU32LE(e.CreatedAt)
bw.WriteU32LE(e.UpdatedAt)
}
// ToStackItem implements stackitem.Convertible interface.
func (e *InvestorEligibility) ToStackItem() (stackitem.Item, error) {
return stackitem.NewStruct([]stackitem.Item{
stackitem.NewBigInteger(big.NewInt(int64(e.VitaID))),
stackitem.NewByteArray(e.Investor.BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(e.Eligibility))),
stackitem.NewBool(e.ScireCompleted),
stackitem.NewBigInteger(big.NewInt(int64(e.RiskScore))),
stackitem.NewBigInteger(big.NewInt(int64(e.TotalInvested))),
stackitem.NewBigInteger(big.NewInt(int64(e.TotalReturns))),
stackitem.NewBigInteger(big.NewInt(int64(e.ActiveInvestments))),
stackitem.NewBigInteger(big.NewInt(int64(e.CompletedInvestments))),
stackitem.NewBool(e.HasViolations),
stackitem.NewBigInteger(big.NewInt(int64(e.ViolationCount))),
stackitem.NewBigInteger(big.NewInt(int64(e.LastActivity))),
stackitem.NewBigInteger(big.NewInt(int64(e.CreatedAt))),
stackitem.NewBigInteger(big.NewInt(int64(e.UpdatedAt))),
}), nil
}
// FromStackItem implements stackitem.Convertible interface.
func (e *InvestorEligibility) FromStackItem(item stackitem.Item) error {
items, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not a struct")
}
if len(items) != 14 {
return fmt.Errorf("wrong number of elements: expected 14, got %d", len(items))
}
vitaID, err := items[0].TryInteger()
if err != nil {
return fmt.Errorf("invalid vitaID: %w", err)
}
e.VitaID = vitaID.Uint64()
investor, err := items[1].TryBytes()
if err != nil {
return fmt.Errorf("invalid investor: %w", err)
}
e.Investor, err = util.Uint160DecodeBytesBE(investor)
if err != nil {
return fmt.Errorf("invalid investor address: %w", err)
}
eligibility, err := items[2].TryInteger()
if err != nil {
return fmt.Errorf("invalid eligibility: %w", err)
}
e.Eligibility = EligibilityType(eligibility.Uint64())
scireCompleted, err := items[3].TryBool()
if err != nil {
return fmt.Errorf("invalid scireCompleted: %w", err)
}
e.ScireCompleted = scireCompleted
riskScore, err := items[4].TryInteger()
if err != nil {
return fmt.Errorf("invalid riskScore: %w", err)
}
e.RiskScore = uint8(riskScore.Uint64())
totalInvested, err := items[5].TryInteger()
if err != nil {
return fmt.Errorf("invalid totalInvested: %w", err)
}
e.TotalInvested = totalInvested.Uint64()
totalReturns, err := items[6].TryInteger()
if err != nil {
return fmt.Errorf("invalid totalReturns: %w", err)
}
e.TotalReturns = totalReturns.Uint64()
activeInvestments, err := items[7].TryInteger()
if err != nil {
return fmt.Errorf("invalid activeInvestments: %w", err)
}
e.ActiveInvestments = activeInvestments.Uint64()
completedInvestments, err := items[8].TryInteger()
if err != nil {
return fmt.Errorf("invalid completedInvestments: %w", err)
}
e.CompletedInvestments = completedInvestments.Uint64()
hasViolations, err := items[9].TryBool()
if err != nil {
return fmt.Errorf("invalid hasViolations: %w", err)
}
e.HasViolations = hasViolations
violationCount, err := items[10].TryInteger()
if err != nil {
return fmt.Errorf("invalid violationCount: %w", err)
}
e.ViolationCount = uint8(violationCount.Uint64())
lastActivity, err := items[11].TryInteger()
if err != nil {
return fmt.Errorf("invalid lastActivity: %w", err)
}
e.LastActivity = uint32(lastActivity.Uint64())
createdAt, err := items[12].TryInteger()
if err != nil {
return fmt.Errorf("invalid createdAt: %w", err)
}
e.CreatedAt = uint32(createdAt.Uint64())
updatedAt, err := items[13].TryInteger()
if err != nil {
return fmt.Errorf("invalid updatedAt: %w", err)
}
e.UpdatedAt = uint32(updatedAt.Uint64())
return nil
}
// EmploymentVerification represents verified employment for EIO eligibility.
type EmploymentVerification struct {
VitaID uint64 // Employee's Vita ID
Employee util.Uint160 // Employee's address
EmployerVitaID uint64 // Employer's Vita ID (organization)
Employer util.Uint160 // Employer's address
Position string // Job position/role
StartDate uint32 // Block height when employment started
EndDate uint32 // Block height when employment ended (0 = current)
IsActive bool // Currently employed
VerifiedAt uint32 // Block height when verified
VerifiedBy util.Uint160 // Verifier's address (RoleInvestmentManager)
}
// DecodeBinary implements the io.Serializable interface.
func (ev *EmploymentVerification) DecodeBinary(br *io.BinReader) {
ev.VitaID = br.ReadU64LE()
br.ReadBytes(ev.Employee[:])
ev.EmployerVitaID = br.ReadU64LE()
br.ReadBytes(ev.Employer[:])
ev.Position = br.ReadString()
ev.StartDate = br.ReadU32LE()
ev.EndDate = br.ReadU32LE()
ev.IsActive = br.ReadBool()
ev.VerifiedAt = br.ReadU32LE()
br.ReadBytes(ev.VerifiedBy[:])
}
// EncodeBinary implements the io.Serializable interface.
func (ev *EmploymentVerification) EncodeBinary(bw *io.BinWriter) {
bw.WriteU64LE(ev.VitaID)
bw.WriteBytes(ev.Employee[:])
bw.WriteU64LE(ev.EmployerVitaID)
bw.WriteBytes(ev.Employer[:])
bw.WriteString(ev.Position)
bw.WriteU32LE(ev.StartDate)
bw.WriteU32LE(ev.EndDate)
bw.WriteBool(ev.IsActive)
bw.WriteU32LE(ev.VerifiedAt)
bw.WriteBytes(ev.VerifiedBy[:])
}
// ToStackItem implements stackitem.Convertible interface.
func (ev *EmploymentVerification) ToStackItem() (stackitem.Item, error) {
return stackitem.NewStruct([]stackitem.Item{
stackitem.NewBigInteger(big.NewInt(int64(ev.VitaID))),
stackitem.NewByteArray(ev.Employee.BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(ev.EmployerVitaID))),
stackitem.NewByteArray(ev.Employer.BytesBE()),
stackitem.NewByteArray([]byte(ev.Position)),
stackitem.NewBigInteger(big.NewInt(int64(ev.StartDate))),
stackitem.NewBigInteger(big.NewInt(int64(ev.EndDate))),
stackitem.NewBool(ev.IsActive),
stackitem.NewBigInteger(big.NewInt(int64(ev.VerifiedAt))),
stackitem.NewByteArray(ev.VerifiedBy.BytesBE()),
}), nil
}
// FromStackItem implements stackitem.Convertible interface.
func (ev *EmploymentVerification) FromStackItem(item stackitem.Item) error {
items, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not a struct")
}
if len(items) != 10 {
return fmt.Errorf("wrong number of elements: expected 10, got %d", len(items))
}
vitaID, err := items[0].TryInteger()
if err != nil {
return fmt.Errorf("invalid vitaID: %w", err)
}
ev.VitaID = vitaID.Uint64()
employee, err := items[1].TryBytes()
if err != nil {
return fmt.Errorf("invalid employee: %w", err)
}
ev.Employee, err = util.Uint160DecodeBytesBE(employee)
if err != nil {
return fmt.Errorf("invalid employee address: %w", err)
}
employerVitaID, err := items[2].TryInteger()
if err != nil {
return fmt.Errorf("invalid employerVitaID: %w", err)
}
ev.EmployerVitaID = employerVitaID.Uint64()
employer, err := items[3].TryBytes()
if err != nil {
return fmt.Errorf("invalid employer: %w", err)
}
ev.Employer, err = util.Uint160DecodeBytesBE(employer)
if err != nil {
return fmt.Errorf("invalid employer address: %w", err)
}
position, err := items[4].TryBytes()
if err != nil {
return fmt.Errorf("invalid position: %w", err)
}
ev.Position = string(position)
startDate, err := items[5].TryInteger()
if err != nil {
return fmt.Errorf("invalid startDate: %w", err)
}
ev.StartDate = uint32(startDate.Uint64())
endDate, err := items[6].TryInteger()
if err != nil {
return fmt.Errorf("invalid endDate: %w", err)
}
ev.EndDate = uint32(endDate.Uint64())
isActive, err := items[7].TryBool()
if err != nil {
return fmt.Errorf("invalid isActive: %w", err)
}
ev.IsActive = isActive
verifiedAt, err := items[8].TryInteger()
if err != nil {
return fmt.Errorf("invalid verifiedAt: %w", err)
}
ev.VerifiedAt = uint32(verifiedAt.Uint64())
verifiedBy, err := items[9].TryBytes()
if err != nil {
return fmt.Errorf("invalid verifiedBy: %w", err)
}
ev.VerifiedBy, err = util.Uint160DecodeBytesBE(verifiedBy)
if err != nil {
return fmt.Errorf("invalid verifiedBy address: %w", err)
}
return nil
}
// ContractorVerification represents verified contractor status for CIO eligibility.
type ContractorVerification struct {
VitaID uint64 // Contractor's Vita ID
Contractor util.Uint160 // Contractor's address
PlatformID string // Platform identifier
Platform util.Uint160 // Platform's address
ContractorID string // Platform-specific contractor ID
StartDate uint32 // Block height when started
EndDate uint32 // Block height when ended (0 = current)
IsActive bool // Currently active
VerifiedAt uint32 // Block height when verified
VerifiedBy util.Uint160 // Verifier's address
}
// DecodeBinary implements the io.Serializable interface.
func (cv *ContractorVerification) DecodeBinary(br *io.BinReader) {
cv.VitaID = br.ReadU64LE()
br.ReadBytes(cv.Contractor[:])
cv.PlatformID = br.ReadString()
br.ReadBytes(cv.Platform[:])
cv.ContractorID = br.ReadString()
cv.StartDate = br.ReadU32LE()
cv.EndDate = br.ReadU32LE()
cv.IsActive = br.ReadBool()
cv.VerifiedAt = br.ReadU32LE()
br.ReadBytes(cv.VerifiedBy[:])
}
// EncodeBinary implements the io.Serializable interface.
func (cv *ContractorVerification) EncodeBinary(bw *io.BinWriter) {
bw.WriteU64LE(cv.VitaID)
bw.WriteBytes(cv.Contractor[:])
bw.WriteString(cv.PlatformID)
bw.WriteBytes(cv.Platform[:])
bw.WriteString(cv.ContractorID)
bw.WriteU32LE(cv.StartDate)
bw.WriteU32LE(cv.EndDate)
bw.WriteBool(cv.IsActive)
bw.WriteU32LE(cv.VerifiedAt)
bw.WriteBytes(cv.VerifiedBy[:])
}
// ToStackItem implements stackitem.Convertible interface.
func (cv *ContractorVerification) ToStackItem() (stackitem.Item, error) {
return stackitem.NewStruct([]stackitem.Item{
stackitem.NewBigInteger(big.NewInt(int64(cv.VitaID))),
stackitem.NewByteArray(cv.Contractor.BytesBE()),
stackitem.NewByteArray([]byte(cv.PlatformID)),
stackitem.NewByteArray(cv.Platform.BytesBE()),
stackitem.NewByteArray([]byte(cv.ContractorID)),
stackitem.NewBigInteger(big.NewInt(int64(cv.StartDate))),
stackitem.NewBigInteger(big.NewInt(int64(cv.EndDate))),
stackitem.NewBool(cv.IsActive),
stackitem.NewBigInteger(big.NewInt(int64(cv.VerifiedAt))),
stackitem.NewByteArray(cv.VerifiedBy.BytesBE()),
}), nil
}
// FromStackItem implements stackitem.Convertible interface.
func (cv *ContractorVerification) FromStackItem(item stackitem.Item) error {
items, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not a struct")
}
if len(items) != 10 {
return fmt.Errorf("wrong number of elements: expected 10, got %d", len(items))
}
vitaID, err := items[0].TryInteger()
if err != nil {
return fmt.Errorf("invalid vitaID: %w", err)
}
cv.VitaID = vitaID.Uint64()
contractor, err := items[1].TryBytes()
if err != nil {
return fmt.Errorf("invalid contractor: %w", err)
}
cv.Contractor, err = util.Uint160DecodeBytesBE(contractor)
if err != nil {
return fmt.Errorf("invalid contractor address: %w", err)
}
platformID, err := items[2].TryBytes()
if err != nil {
return fmt.Errorf("invalid platformID: %w", err)
}
cv.PlatformID = string(platformID)
platform, err := items[3].TryBytes()
if err != nil {
return fmt.Errorf("invalid platform: %w", err)
}
cv.Platform, err = util.Uint160DecodeBytesBE(platform)
if err != nil {
return fmt.Errorf("invalid platform address: %w", err)
}
contractorID, err := items[4].TryBytes()
if err != nil {
return fmt.Errorf("invalid contractorID: %w", err)
}
cv.ContractorID = string(contractorID)
startDate, err := items[5].TryInteger()
if err != nil {
return fmt.Errorf("invalid startDate: %w", err)
}
cv.StartDate = uint32(startDate.Uint64())
endDate, err := items[6].TryInteger()
if err != nil {
return fmt.Errorf("invalid endDate: %w", err)
}
cv.EndDate = uint32(endDate.Uint64())
isActive, err := items[7].TryBool()
if err != nil {
return fmt.Errorf("invalid isActive: %w", err)
}
cv.IsActive = isActive
verifiedAt, err := items[8].TryInteger()
if err != nil {
return fmt.Errorf("invalid verifiedAt: %w", err)
}
cv.VerifiedAt = uint32(verifiedAt.Uint64())
verifiedBy, err := items[9].TryBytes()
if err != nil {
return fmt.Errorf("invalid verifiedBy: %w", err)
}
cv.VerifiedBy, err = util.Uint160DecodeBytesBE(verifiedBy)
if err != nil {
return fmt.Errorf("invalid verifiedBy address: %w", err)
}
return nil
}
// InvestmentViolation represents a recorded investment abuse violation.
type InvestmentViolation struct {
ID uint64 // Unique violation ID
VitaID uint64 // Violator's Vita ID
Violator util.Uint160 // Violator's address
OpportunityID uint64 // Related opportunity (0 = general)
ViolationType string // Type of violation
Description string // Violation description
EvidenceHash util.Uint256 // Hash of evidence (off-chain)
Penalty uint64 // VTS penalty applied
ReportedBy util.Uint160 // Reporter's address
ReportedAt uint32 // Block height when reported
ResolvedAt uint32 // Block height when resolved (0 = pending)
Resolution string // Resolution description
}
// DecodeBinary implements the io.Serializable interface.
func (v *InvestmentViolation) DecodeBinary(br *io.BinReader) {
v.ID = br.ReadU64LE()
v.VitaID = br.ReadU64LE()
br.ReadBytes(v.Violator[:])
v.OpportunityID = br.ReadU64LE()
v.ViolationType = br.ReadString()
v.Description = br.ReadString()
br.ReadBytes(v.EvidenceHash[:])
v.Penalty = br.ReadU64LE()
br.ReadBytes(v.ReportedBy[:])
v.ReportedAt = br.ReadU32LE()
v.ResolvedAt = br.ReadU32LE()
v.Resolution = br.ReadString()
}
// EncodeBinary implements the io.Serializable interface.
func (v *InvestmentViolation) EncodeBinary(bw *io.BinWriter) {
bw.WriteU64LE(v.ID)
bw.WriteU64LE(v.VitaID)
bw.WriteBytes(v.Violator[:])
bw.WriteU64LE(v.OpportunityID)
bw.WriteString(v.ViolationType)
bw.WriteString(v.Description)
bw.WriteBytes(v.EvidenceHash[:])
bw.WriteU64LE(v.Penalty)
bw.WriteBytes(v.ReportedBy[:])
bw.WriteU32LE(v.ReportedAt)
bw.WriteU32LE(v.ResolvedAt)
bw.WriteString(v.Resolution)
}
// ToStackItem implements stackitem.Convertible interface.
func (v *InvestmentViolation) ToStackItem() (stackitem.Item, error) {
return stackitem.NewStruct([]stackitem.Item{
stackitem.NewBigInteger(big.NewInt(int64(v.ID))),
stackitem.NewBigInteger(big.NewInt(int64(v.VitaID))),
stackitem.NewByteArray(v.Violator.BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(v.OpportunityID))),
stackitem.NewByteArray([]byte(v.ViolationType)),
stackitem.NewByteArray([]byte(v.Description)),
stackitem.NewByteArray(v.EvidenceHash.BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(v.Penalty))),
stackitem.NewByteArray(v.ReportedBy.BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(v.ReportedAt))),
stackitem.NewBigInteger(big.NewInt(int64(v.ResolvedAt))),
stackitem.NewByteArray([]byte(v.Resolution)),
}), nil
}
// FromStackItem implements stackitem.Convertible interface.
func (v *InvestmentViolation) FromStackItem(item stackitem.Item) error {
items, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not a struct")
}
if len(items) != 12 {
return fmt.Errorf("wrong number of elements: expected 12, got %d", len(items))
}
id, err := items[0].TryInteger()
if err != nil {
return fmt.Errorf("invalid id: %w", err)
}
v.ID = id.Uint64()
vitaID, err := items[1].TryInteger()
if err != nil {
return fmt.Errorf("invalid vitaID: %w", err)
}
v.VitaID = vitaID.Uint64()
violator, err := items[2].TryBytes()
if err != nil {
return fmt.Errorf("invalid violator: %w", err)
}
v.Violator, err = util.Uint160DecodeBytesBE(violator)
if err != nil {
return fmt.Errorf("invalid violator address: %w", err)
}
oppID, err := items[3].TryInteger()
if err != nil {
return fmt.Errorf("invalid opportunityID: %w", err)
}
v.OpportunityID = oppID.Uint64()
violationType, err := items[4].TryBytes()
if err != nil {
return fmt.Errorf("invalid violationType: %w", err)
}
v.ViolationType = string(violationType)
description, err := items[5].TryBytes()
if err != nil {
return fmt.Errorf("invalid description: %w", err)
}
v.Description = string(description)
evidenceHash, err := items[6].TryBytes()
if err != nil {
return fmt.Errorf("invalid evidenceHash: %w", err)
}
v.EvidenceHash, err = util.Uint256DecodeBytesBE(evidenceHash)
if err != nil {
return fmt.Errorf("invalid evidenceHash: %w", err)
}
penalty, err := items[7].TryInteger()
if err != nil {
return fmt.Errorf("invalid penalty: %w", err)
}
v.Penalty = penalty.Uint64()
reportedBy, err := items[8].TryBytes()
if err != nil {
return fmt.Errorf("invalid reportedBy: %w", err)
}
v.ReportedBy, err = util.Uint160DecodeBytesBE(reportedBy)
if err != nil {
return fmt.Errorf("invalid reportedBy address: %w", err)
}
reportedAt, err := items[9].TryInteger()
if err != nil {
return fmt.Errorf("invalid reportedAt: %w", err)
}
v.ReportedAt = uint32(reportedAt.Uint64())
resolvedAt, err := items[10].TryInteger()
if err != nil {
return fmt.Errorf("invalid resolvedAt: %w", err)
}
v.ResolvedAt = uint32(resolvedAt.Uint64())
resolution, err := items[11].TryBytes()
if err != nil {
return fmt.Errorf("invalid resolution: %w", err)
}
v.Resolution = string(resolution)
return nil
}
// CollocatioConfig represents configurable parameters for the investment contract.
type CollocatioConfig struct {
// Minimum requirements
MinPIOParticipants uint64 // Default minimum for PIO (default: 100)
MinEIOParticipants uint64 // Default minimum for EIO (default: 10)
MinCIOParticipants uint64 // Default minimum for CIO (default: 25)
// Investment limits
DefaultMinInvestment uint64 // Default minimum investment in VTS (default: 100)
MaxIndividualCap uint64 // Maximum any individual can invest (default: 1000000)
WealthConcentration uint64 // Max % of pool by single investor (basis points, default: 500 = 5%)
// Fees
CreationFee uint64 // VTS fee to create opportunity
InvestmentFee uint64 // Fee per investment (basis points, default: 50 = 0.5%)
WithdrawalPenalty uint64 // Early withdrawal penalty (basis points, default: 200 = 2%)
// Timing
MinVotingPeriod uint32 // Minimum blocks for voting (default: 10000)
MinInvestmentPeriod uint32 // Minimum blocks for investment window (default: 20000)
MinMaturityPeriod uint32 // Minimum blocks until maturity (default: 50000)
// Violation thresholds
MaxViolationsBeforeBan uint8 // Violations before permanent ban (default: 3)
ViolationCooldown uint32 // Blocks before violation expires (default: 1000000)
// Commit-reveal timing (anti-front-running)
CommitRevealDelay uint32 // Min blocks between commit and reveal (default: 10)
CommitRevealWindow uint32 // Max blocks to reveal after delay (default: 1000)
}
// DecodeBinary implements the io.Serializable interface.
func (c *CollocatioConfig) DecodeBinary(br *io.BinReader) {
c.MinPIOParticipants = br.ReadU64LE()
c.MinEIOParticipants = br.ReadU64LE()
c.MinCIOParticipants = br.ReadU64LE()
c.DefaultMinInvestment = br.ReadU64LE()
c.MaxIndividualCap = br.ReadU64LE()
c.WealthConcentration = br.ReadU64LE()
c.CreationFee = br.ReadU64LE()
c.InvestmentFee = br.ReadU64LE()
c.WithdrawalPenalty = br.ReadU64LE()
c.MinVotingPeriod = br.ReadU32LE()
c.MinInvestmentPeriod = br.ReadU32LE()
c.MinMaturityPeriod = br.ReadU32LE()
c.MaxViolationsBeforeBan = br.ReadB()
c.ViolationCooldown = br.ReadU32LE()
c.CommitRevealDelay = br.ReadU32LE()
c.CommitRevealWindow = br.ReadU32LE()
}
// EncodeBinary implements the io.Serializable interface.
func (c *CollocatioConfig) EncodeBinary(bw *io.BinWriter) {
bw.WriteU64LE(c.MinPIOParticipants)
bw.WriteU64LE(c.MinEIOParticipants)
bw.WriteU64LE(c.MinCIOParticipants)
bw.WriteU64LE(c.DefaultMinInvestment)
bw.WriteU64LE(c.MaxIndividualCap)
bw.WriteU64LE(c.WealthConcentration)
bw.WriteU64LE(c.CreationFee)
bw.WriteU64LE(c.InvestmentFee)
bw.WriteU64LE(c.WithdrawalPenalty)
bw.WriteU32LE(c.MinVotingPeriod)
bw.WriteU32LE(c.MinInvestmentPeriod)
bw.WriteU32LE(c.MinMaturityPeriod)
bw.WriteB(c.MaxViolationsBeforeBan)
bw.WriteU32LE(c.ViolationCooldown)
bw.WriteU32LE(c.CommitRevealDelay)
bw.WriteU32LE(c.CommitRevealWindow)
}
// ToStackItem implements stackitem.Convertible interface.
func (c *CollocatioConfig) ToStackItem() (stackitem.Item, error) {
return stackitem.NewStruct([]stackitem.Item{
stackitem.NewBigInteger(big.NewInt(int64(c.MinPIOParticipants))),
stackitem.NewBigInteger(big.NewInt(int64(c.MinEIOParticipants))),
stackitem.NewBigInteger(big.NewInt(int64(c.MinCIOParticipants))),
stackitem.NewBigInteger(big.NewInt(int64(c.DefaultMinInvestment))),
stackitem.NewBigInteger(big.NewInt(int64(c.MaxIndividualCap))),
stackitem.NewBigInteger(big.NewInt(int64(c.WealthConcentration))),
stackitem.NewBigInteger(big.NewInt(int64(c.CreationFee))),
stackitem.NewBigInteger(big.NewInt(int64(c.InvestmentFee))),
stackitem.NewBigInteger(big.NewInt(int64(c.WithdrawalPenalty))),
stackitem.NewBigInteger(big.NewInt(int64(c.MinVotingPeriod))),
stackitem.NewBigInteger(big.NewInt(int64(c.MinInvestmentPeriod))),
stackitem.NewBigInteger(big.NewInt(int64(c.MinMaturityPeriod))),
stackitem.NewBigInteger(big.NewInt(int64(c.MaxViolationsBeforeBan))),
stackitem.NewBigInteger(big.NewInt(int64(c.ViolationCooldown))),
stackitem.NewBigInteger(big.NewInt(int64(c.CommitRevealDelay))),
stackitem.NewBigInteger(big.NewInt(int64(c.CommitRevealWindow))),
}), nil
}
// FromStackItem implements stackitem.Convertible interface.
func (c *CollocatioConfig) FromStackItem(item stackitem.Item) error {
items, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not a struct")
}
if len(items) != 16 {
return fmt.Errorf("wrong number of elements: expected 16, got %d", len(items))
}
minPIO, err := items[0].TryInteger()
if err != nil {
return fmt.Errorf("invalid minPIOParticipants: %w", err)
}
c.MinPIOParticipants = minPIO.Uint64()
minEIO, err := items[1].TryInteger()
if err != nil {
return fmt.Errorf("invalid minEIOParticipants: %w", err)
}
c.MinEIOParticipants = minEIO.Uint64()
minCIO, err := items[2].TryInteger()
if err != nil {
return fmt.Errorf("invalid minCIOParticipants: %w", err)
}
c.MinCIOParticipants = minCIO.Uint64()
defaultMin, err := items[3].TryInteger()
if err != nil {
return fmt.Errorf("invalid defaultMinInvestment: %w", err)
}
c.DefaultMinInvestment = defaultMin.Uint64()
maxCap, err := items[4].TryInteger()
if err != nil {
return fmt.Errorf("invalid maxIndividualCap: %w", err)
}
c.MaxIndividualCap = maxCap.Uint64()
wealthConc, err := items[5].TryInteger()
if err != nil {
return fmt.Errorf("invalid wealthConcentration: %w", err)
}
c.WealthConcentration = wealthConc.Uint64()
creationFee, err := items[6].TryInteger()
if err != nil {
return fmt.Errorf("invalid creationFee: %w", err)
}
c.CreationFee = creationFee.Uint64()
investmentFee, err := items[7].TryInteger()
if err != nil {
return fmt.Errorf("invalid investmentFee: %w", err)
}
c.InvestmentFee = investmentFee.Uint64()
withdrawalPenalty, err := items[8].TryInteger()
if err != nil {
return fmt.Errorf("invalid withdrawalPenalty: %w", err)
}
c.WithdrawalPenalty = withdrawalPenalty.Uint64()
minVoting, err := items[9].TryInteger()
if err != nil {
return fmt.Errorf("invalid minVotingPeriod: %w", err)
}
c.MinVotingPeriod = uint32(minVoting.Uint64())
minInvestment, err := items[10].TryInteger()
if err != nil {
return fmt.Errorf("invalid minInvestmentPeriod: %w", err)
}
c.MinInvestmentPeriod = uint32(minInvestment.Uint64())
minMaturity, err := items[11].TryInteger()
if err != nil {
return fmt.Errorf("invalid minMaturityPeriod: %w", err)
}
c.MinMaturityPeriod = uint32(minMaturity.Uint64())
maxViolations, err := items[12].TryInteger()
if err != nil {
return fmt.Errorf("invalid maxViolationsBeforeBan: %w", err)
}
c.MaxViolationsBeforeBan = uint8(maxViolations.Uint64())
cooldown, err := items[13].TryInteger()
if err != nil {
return fmt.Errorf("invalid violationCooldown: %w", err)
}
c.ViolationCooldown = uint32(cooldown.Uint64())
commitDelay, err := items[14].TryInteger()
if err != nil {
return fmt.Errorf("invalid commitRevealDelay: %w", err)
}
c.CommitRevealDelay = uint32(commitDelay.Uint64())
commitWindow, err := items[15].TryInteger()
if err != nil {
return fmt.Errorf("invalid commitRevealWindow: %w", err)
}
c.CommitRevealWindow = uint32(commitWindow.Uint64())
return nil
}
// DefaultCollocatioConfig returns the default configuration.
func DefaultCollocatioConfig() CollocatioConfig {
return CollocatioConfig{
MinPIOParticipants: 100,
MinEIOParticipants: 10,
MinCIOParticipants: 25,
DefaultMinInvestment: 100_00000000, // 100 VTS
MaxIndividualCap: 1_000_000_00000000, // 1M VTS
WealthConcentration: 500, // 5%
CreationFee: 1000_00000000, // 1000 VTS
InvestmentFee: 50, // 0.5%
WithdrawalPenalty: 200, // 2%
MinVotingPeriod: 10000, // ~10000 blocks
MinInvestmentPeriod: 20000, // ~20000 blocks
MinMaturityPeriod: 50000, // ~50000 blocks
MaxViolationsBeforeBan: 3,
ViolationCooldown: 1000000, // ~1M blocks
CommitRevealDelay: 10, // ~10 blocks minimum between commit and reveal
CommitRevealWindow: 1000, // ~1000 blocks to reveal after delay
}
}
// CommitmentStatus represents the status of an investment commitment.
type CommitmentStatus uint8
// Commitment statuses.
const (
CommitmentPending CommitmentStatus = 0 // Awaiting reveal
CommitmentRevealed CommitmentStatus = 1 // Successfully revealed and invested
CommitmentExpired CommitmentStatus = 2 // Expired without reveal
CommitmentCanceled CommitmentStatus = 3 // Canceled by investor
)
// InvestmentCommitment represents a commit-reveal commitment for investment.
// This prevents front-running attacks by hiding the investment amount until reveal.
type InvestmentCommitment struct {
ID uint64 // Unique commitment ID
OpportunityID uint64 // Opportunity being invested in
VitaID uint64 // Investor's Vita ID
Investor util.Uint160 // Investor's address
Commitment util.Uint256 // hash(amount || nonce || investor)
Status CommitmentStatus // Current status
CommittedAt uint32 // Block height when committed
RevealDeadline uint32 // Block height by which reveal must occur
RevealedAmount uint64 // Amount revealed (0 until revealed)
InvestmentID uint64 // Resulting investment ID (0 until revealed)
}
// DecodeBinary implements the io.Serializable interface.
func (c *InvestmentCommitment) DecodeBinary(br *io.BinReader) {
c.ID = br.ReadU64LE()
c.OpportunityID = br.ReadU64LE()
c.VitaID = br.ReadU64LE()
br.ReadBytes(c.Investor[:])
br.ReadBytes(c.Commitment[:])
c.Status = CommitmentStatus(br.ReadB())
c.CommittedAt = br.ReadU32LE()
c.RevealDeadline = br.ReadU32LE()
c.RevealedAmount = br.ReadU64LE()
c.InvestmentID = br.ReadU64LE()
}
// EncodeBinary implements the io.Serializable interface.
func (c *InvestmentCommitment) EncodeBinary(bw *io.BinWriter) {
bw.WriteU64LE(c.ID)
bw.WriteU64LE(c.OpportunityID)
bw.WriteU64LE(c.VitaID)
bw.WriteBytes(c.Investor[:])
bw.WriteBytes(c.Commitment[:])
bw.WriteB(byte(c.Status))
bw.WriteU32LE(c.CommittedAt)
bw.WriteU32LE(c.RevealDeadline)
bw.WriteU64LE(c.RevealedAmount)
bw.WriteU64LE(c.InvestmentID)
}
// ToStackItem implements stackitem.Convertible interface.
func (c *InvestmentCommitment) ToStackItem() (stackitem.Item, error) {
return stackitem.NewStruct([]stackitem.Item{
stackitem.NewBigInteger(big.NewInt(int64(c.ID))),
stackitem.NewBigInteger(big.NewInt(int64(c.OpportunityID))),
stackitem.NewBigInteger(big.NewInt(int64(c.VitaID))),
stackitem.NewByteArray(c.Investor.BytesBE()),
stackitem.NewByteArray(c.Commitment.BytesBE()),
stackitem.NewBigInteger(big.NewInt(int64(c.Status))),
stackitem.NewBigInteger(big.NewInt(int64(c.CommittedAt))),
stackitem.NewBigInteger(big.NewInt(int64(c.RevealDeadline))),
stackitem.NewBigInteger(big.NewInt(int64(c.RevealedAmount))),
stackitem.NewBigInteger(big.NewInt(int64(c.InvestmentID))),
}), nil
}
// FromStackItem implements stackitem.Convertible interface.
func (c *InvestmentCommitment) FromStackItem(item stackitem.Item) error {
items, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not a struct")
}
if len(items) != 10 {
return fmt.Errorf("wrong number of elements: expected 10, got %d", len(items))
}
id, err := items[0].TryInteger()
if err != nil {
return fmt.Errorf("invalid id: %w", err)
}
c.ID = id.Uint64()
oppID, err := items[1].TryInteger()
if err != nil {
return fmt.Errorf("invalid opportunityID: %w", err)
}
c.OpportunityID = oppID.Uint64()
vitaID, err := items[2].TryInteger()
if err != nil {
return fmt.Errorf("invalid vitaID: %w", err)
}
c.VitaID = vitaID.Uint64()
investor, err := items[3].TryBytes()
if err != nil {
return fmt.Errorf("invalid investor: %w", err)
}
c.Investor, err = util.Uint160DecodeBytesBE(investor)
if err != nil {
return fmt.Errorf("invalid investor address: %w", err)
}
commitment, err := items[4].TryBytes()
if err != nil {
return fmt.Errorf("invalid commitment: %w", err)
}
c.Commitment, err = util.Uint256DecodeBytesBE(commitment)
if err != nil {
return fmt.Errorf("invalid commitment hash: %w", err)
}
status, err := items[5].TryInteger()
if err != nil {
return fmt.Errorf("invalid status: %w", err)
}
c.Status = CommitmentStatus(status.Uint64())
committedAt, err := items[6].TryInteger()
if err != nil {
return fmt.Errorf("invalid committedAt: %w", err)
}
c.CommittedAt = uint32(committedAt.Uint64())
revealDeadline, err := items[7].TryInteger()
if err != nil {
return fmt.Errorf("invalid revealDeadline: %w", err)
}
c.RevealDeadline = uint32(revealDeadline.Uint64())
revealedAmount, err := items[8].TryInteger()
if err != nil {
return fmt.Errorf("invalid revealedAmount: %w", err)
}
c.RevealedAmount = revealedAmount.Uint64()
investmentID, err := items[9].TryInteger()
if err != nil {
return fmt.Errorf("invalid investmentID: %w", err)
}
c.InvestmentID = investmentID.Uint64()
return nil
}