Complete Collocatio native contract for PIO/EIO/CIO investments
Implement comprehensive investment infrastructure with three tiers: - PIO (Public Investment Opportunity): Universal citizen access - EIO (Employee Investment Opportunity): Workplace democracy - CIO (Contractor Investment Opportunity): Gig economy empowerment New Features: Violation System: - recordViolation: Record violations with evidence and penalties - resolveViolation: Resolve violations with committee authority - getViolation: Query violation records - Auto-ban after MaxViolationsBeforeBan threshold Employment Verification (EIO eligibility): - verifyEmployment: Register employee-employer relationships - revokeEmployment: End employment verification - getEmploymentStatus: Query employment records - EIO eligibility requires active employment Contractor Verification (CIO eligibility): - verifyContractor: Register contractor-platform relationships - revokeContractor: End contractor verification - getContractorStatus: Query contractor records - CIO eligibility requires active contractor status Returns Distribution: - distributeReturns: Proportional returns to all investors - cancelOpportunity: Refund investors on cancellation Education Integration: - completeInvestmentEducation: Mark investor education complete - Scire certification tracking for eligibility Query Methods: - getInvestmentsByOpportunity: List investments per opportunity - getInvestmentsByInvestor: List investments per investor - getOpportunitiesByType: Filter by PIO/EIO/CIO - getOpportunitiesByStatus: Filter by lifecycle status Lifecycle Automation (PostPersist): - Auto-close opportunities past investment deadline - Auto-fail opportunities below minimum participants - Runs every 100 blocks for performance State Serialization: - Add ToStackItem/FromStackItem for EmploymentVerification - Add ToStackItem/FromStackItem for ContractorVerification - Add ToStackItem/FromStackItem for InvestmentViolation Tests: - 14 test cases for config, counters, queries, error handling 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
c07696a18c
commit
5678ae0e46
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,192 @@
|
||||||
|
package native_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/tutus-one/tutus-chain/pkg/core/native/nativenames"
|
||||||
|
"github.com/tutus-one/tutus-chain/pkg/core/state"
|
||||||
|
"github.com/tutus-one/tutus-chain/pkg/neotest"
|
||||||
|
"github.com/tutus-one/tutus-chain/pkg/vm/stackitem"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newCollocatioClient(t *testing.T) *neotest.ContractInvoker {
|
||||||
|
return newNativeClient(t, nativenames.Collocatio)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCollocatio_GetConfig tests the getConfig method.
|
||||||
|
func TestCollocatio_GetConfig(t *testing.T) {
|
||||||
|
c := newCollocatioClient(t)
|
||||||
|
|
||||||
|
// Get default config
|
||||||
|
c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) {
|
||||||
|
require.Equal(t, 1, len(stack))
|
||||||
|
arr, ok := stack[0].Value().([]stackitem.Item)
|
||||||
|
require.True(t, ok, "expected array result")
|
||||||
|
require.GreaterOrEqual(t, len(arr), 13) // CollocatioConfig has 13 fields
|
||||||
|
}, "getConfig")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCollocatio_GetOpportunityCount tests the getOpportunityCount method.
|
||||||
|
func TestCollocatio_GetOpportunityCount(t *testing.T) {
|
||||||
|
c := newCollocatioClient(t)
|
||||||
|
|
||||||
|
// Initially should be 0
|
||||||
|
c.Invoke(t, 0, "getOpportunityCount")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCollocatio_GetInvestmentCount tests the getInvestmentCount method.
|
||||||
|
func TestCollocatio_GetInvestmentCount(t *testing.T) {
|
||||||
|
c := newCollocatioClient(t)
|
||||||
|
|
||||||
|
// Initially should be 0
|
||||||
|
c.Invoke(t, 0, "getInvestmentCount")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCollocatio_GetOpportunity_NonExistent tests getting a non-existent opportunity.
|
||||||
|
func TestCollocatio_GetOpportunity_NonExistent(t *testing.T) {
|
||||||
|
c := newCollocatioClient(t)
|
||||||
|
|
||||||
|
// Non-existent opportunity should return null
|
||||||
|
c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) {
|
||||||
|
require.Equal(t, 1, len(stack))
|
||||||
|
require.Nil(t, stack[0].Value(), "expected null for non-existent opportunity")
|
||||||
|
}, "getOpportunity", 999)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCollocatio_GetInvestment_NonExistent tests getting a non-existent investment.
|
||||||
|
func TestCollocatio_GetInvestment_NonExistent(t *testing.T) {
|
||||||
|
c := newCollocatioClient(t)
|
||||||
|
|
||||||
|
// Non-existent investment should return null
|
||||||
|
c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) {
|
||||||
|
require.Equal(t, 1, len(stack))
|
||||||
|
require.Nil(t, stack[0].Value(), "expected null for non-existent investment")
|
||||||
|
}, "getInvestment", 999)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCollocatio_GetEligibility_NonExistent tests getting eligibility for non-existent investor.
|
||||||
|
func TestCollocatio_GetEligibility_NonExistent(t *testing.T) {
|
||||||
|
c := newCollocatioClient(t)
|
||||||
|
e := c.Executor
|
||||||
|
|
||||||
|
acc := e.NewAccount(t)
|
||||||
|
|
||||||
|
// Non-existent eligibility should return null
|
||||||
|
c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) {
|
||||||
|
require.Equal(t, 1, len(stack))
|
||||||
|
require.Nil(t, stack[0].Value(), "expected null for non-existent eligibility")
|
||||||
|
}, "getEligibility", acc.ScriptHash())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCollocatio_IsEligible_NoVita tests eligibility check without Vita.
|
||||||
|
func TestCollocatio_IsEligible_NoVita(t *testing.T) {
|
||||||
|
c := newCollocatioClient(t)
|
||||||
|
e := c.Executor
|
||||||
|
|
||||||
|
acc := e.NewAccount(t)
|
||||||
|
|
||||||
|
// Should return false for all opportunity types without Vita
|
||||||
|
c.Invoke(t, false, "isEligible", acc.ScriptHash(), int64(state.OpportunityPIO))
|
||||||
|
c.Invoke(t, false, "isEligible", acc.ScriptHash(), int64(state.OpportunityEIO))
|
||||||
|
c.Invoke(t, false, "isEligible", acc.ScriptHash(), int64(state.OpportunityCIO))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCollocatio_GetViolation_NonExistent tests getting a non-existent violation.
|
||||||
|
func TestCollocatio_GetViolation_NonExistent(t *testing.T) {
|
||||||
|
c := newCollocatioClient(t)
|
||||||
|
|
||||||
|
// Non-existent violation should return null
|
||||||
|
c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) {
|
||||||
|
require.Equal(t, 1, len(stack))
|
||||||
|
require.Nil(t, stack[0].Value(), "expected null for non-existent violation")
|
||||||
|
}, "getViolation", 999)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCollocatio_GetInvestmentsByOpportunity_Empty tests getting investments for non-existent opportunity.
|
||||||
|
func TestCollocatio_GetInvestmentsByOpportunity_Empty(t *testing.T) {
|
||||||
|
c := newCollocatioClient(t)
|
||||||
|
|
||||||
|
// Should return empty array
|
||||||
|
c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) {
|
||||||
|
require.Equal(t, 1, len(stack))
|
||||||
|
arr, ok := stack[0].Value().([]stackitem.Item)
|
||||||
|
require.True(t, ok, "expected array result")
|
||||||
|
require.Equal(t, 0, len(arr), "expected empty array")
|
||||||
|
}, "getInvestmentsByOpportunity", 999)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCollocatio_GetOpportunitiesByType_Empty tests getting opportunities by type when none exist.
|
||||||
|
func TestCollocatio_GetOpportunitiesByType_Empty(t *testing.T) {
|
||||||
|
c := newCollocatioClient(t)
|
||||||
|
|
||||||
|
// Should return empty array for each type
|
||||||
|
for _, oppType := range []state.OpportunityType{state.OpportunityPIO, state.OpportunityEIO, state.OpportunityCIO} {
|
||||||
|
c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) {
|
||||||
|
require.Equal(t, 1, len(stack))
|
||||||
|
arr, ok := stack[0].Value().([]stackitem.Item)
|
||||||
|
require.True(t, ok, "expected array result")
|
||||||
|
require.Equal(t, 0, len(arr), "expected empty array")
|
||||||
|
}, "getOpportunitiesByType", int64(oppType))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCollocatio_GetOpportunitiesByStatus_Empty tests getting opportunities by status when none exist.
|
||||||
|
func TestCollocatio_GetOpportunitiesByStatus_Empty(t *testing.T) {
|
||||||
|
c := newCollocatioClient(t)
|
||||||
|
|
||||||
|
// Should return empty array for each status
|
||||||
|
for _, status := range []state.OpportunityStatus{
|
||||||
|
state.OpportunityDraft,
|
||||||
|
state.OpportunityVoting,
|
||||||
|
state.OpportunityActive,
|
||||||
|
state.OpportunityClosed,
|
||||||
|
state.OpportunityCompleted,
|
||||||
|
state.OpportunityCancelled,
|
||||||
|
state.OpportunityFailed,
|
||||||
|
} {
|
||||||
|
c.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) {
|
||||||
|
require.Equal(t, 1, len(stack))
|
||||||
|
arr, ok := stack[0].Value().([]stackitem.Item)
|
||||||
|
require.True(t, ok, "expected array result")
|
||||||
|
require.Equal(t, 0, len(arr), "expected empty array")
|
||||||
|
}, "getOpportunitiesByStatus", int64(status))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCollocatio_ActivateOpportunity_NonExistent tests activating non-existent opportunity.
|
||||||
|
func TestCollocatio_ActivateOpportunity_NonExistent(t *testing.T) {
|
||||||
|
c := newCollocatioClient(t)
|
||||||
|
|
||||||
|
// Should fail - opportunity doesn't exist
|
||||||
|
c.InvokeFail(t, "opportunity not found", "activateOpportunity", 999)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCollocatio_DistributeReturns_NonExistent tests distributing returns for non-existent opportunity.
|
||||||
|
func TestCollocatio_DistributeReturns_NonExistent(t *testing.T) {
|
||||||
|
c := newCollocatioClient(t)
|
||||||
|
|
||||||
|
// Should fail - opportunity doesn't exist
|
||||||
|
c.InvokeFail(t, "opportunity not found", "distributeReturns", 999, 1000_00000000)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCollocatio_CancelOpportunity_NonExistent tests canceling non-existent opportunity.
|
||||||
|
func TestCollocatio_CancelOpportunity_NonExistent(t *testing.T) {
|
||||||
|
c := newCollocatioClient(t)
|
||||||
|
|
||||||
|
// Should fail - opportunity doesn't exist
|
||||||
|
c.InvokeFail(t, "opportunity not found", "cancelOpportunity", 999)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCollocatio_ResolveViolation_NonExistent tests resolving non-existent violation.
|
||||||
|
func TestCollocatio_ResolveViolation_NonExistent(t *testing.T) {
|
||||||
|
c := newCollocatioClient(t)
|
||||||
|
|
||||||
|
// Should fail - violation doesn't exist
|
||||||
|
c.InvokeFail(t, "violation not found", "resolveViolation", 999, "Resolved after investigation")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: Tests that require a Vita token (like createOpportunity, invest, verifyEmployment, etc.)
|
||||||
|
// are more complex because they require a deployed helper contract since the native contracts
|
||||||
|
// use GetCallingScriptHash() for authorization, which returns the transaction script hash for
|
||||||
|
// direct calls rather than the signer's account. These would require cross-contract integration tests.
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -664,6 +664,104 @@ func (ev *EmploymentVerification) EncodeBinary(bw *io.BinWriter) {
|
||||||
bw.WriteBytes(ev.VerifiedBy[:])
|
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.
|
// ContractorVerification represents verified contractor status for CIO eligibility.
|
||||||
type ContractorVerification struct {
|
type ContractorVerification struct {
|
||||||
VitaID uint64 // Contractor's Vita ID
|
VitaID uint64 // Contractor's Vita ID
|
||||||
|
|
@ -706,6 +804,104 @@ func (cv *ContractorVerification) EncodeBinary(bw *io.BinWriter) {
|
||||||
bw.WriteBytes(cv.VerifiedBy[:])
|
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.
|
// InvestmentViolation represents a recorded investment abuse violation.
|
||||||
type InvestmentViolation struct {
|
type InvestmentViolation struct {
|
||||||
ID uint64 // Unique violation ID
|
ID uint64 // Unique violation ID
|
||||||
|
|
@ -754,6 +950,118 @@ func (v *InvestmentViolation) EncodeBinary(bw *io.BinWriter) {
|
||||||
bw.WriteString(v.Resolution)
|
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.
|
// CollocatioConfig represents configurable parameters for the investment contract.
|
||||||
type CollocatioConfig struct {
|
type CollocatioConfig struct {
|
||||||
// Minimum requirements
|
// Minimum requirements
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue