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[:]) } // 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[:]) } // 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) } // 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) } // 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() } // 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) } // 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))), }), 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) != 14 { return fmt.Errorf("wrong number of elements: expected 14, 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()) 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 } }