Add Tribute native contract for anti-hoarding economics
Implement resource velocity economics infrastructure to prevent wealth hoarding and encourage productive circulation: - Velocity Accounts: Track spending patterns and circulation metrics for each Vita holder, measuring economic participation - Tribute Assessment: Progressive contribution based on idle wealth - Configurable thresholds (default: 10% of GDP per capita) - Annual assessment periods with appeal process - Exemptions for essential holdings (primary residence, etc.) - Circulation Incentives: Rewards for economic velocity - Spending bonuses for productive economic activity - Investment incentives for job-creating ventures - Community contribution recognition - Redistribution: Equitable allocation of collected tribute - Universal Basic Dividend to all citizens - Priority for education, healthcare, infrastructure - Transparent on-chain accounting - Cross-contract integration: - Vita: Identity verification - VTS: Payment processing - Lex: Property rights enforcement - RoleRegistry: RoleTributeAdmin (ID 23) Contract ID: -21 🤖 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
0e980e7342
commit
6d79a79672
|
|
@ -0,0 +1,293 @@
|
|||
package native_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/tutus-one/tutus-chain/pkg/core/native/nativenames"
|
||||
"github.com/tutus-one/tutus-chain/pkg/neotest"
|
||||
"github.com/tutus-one/tutus-chain/pkg/vm/stackitem"
|
||||
)
|
||||
|
||||
func newTributeClient(t *testing.T) *neotest.ContractInvoker {
|
||||
return newNativeClient(t, nativenames.Tribute)
|
||||
}
|
||||
|
||||
// TestTribute_GetConfig tests the getConfig method.
|
||||
func TestTribute_GetConfig(t *testing.T) {
|
||||
c := newTributeClient(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), 15) // TributeConfig has 15 fields
|
||||
}, "getConfig")
|
||||
}
|
||||
|
||||
// TestTribute_GetTotalAccounts tests the getTotalAccounts method.
|
||||
func TestTribute_GetTotalAccounts(t *testing.T) {
|
||||
c := newTributeClient(t)
|
||||
|
||||
// Initially should be 0
|
||||
c.Invoke(t, 0, "getTotalAccounts")
|
||||
}
|
||||
|
||||
// TestTribute_GetTotalAssessments tests the getTotalAssessments method.
|
||||
func TestTribute_GetTotalAssessments(t *testing.T) {
|
||||
c := newTributeClient(t)
|
||||
|
||||
// Initially should be 0
|
||||
c.Invoke(t, 0, "getTotalAssessments")
|
||||
}
|
||||
|
||||
// TestTribute_GetTotalIncentives tests the getTotalIncentives method.
|
||||
func TestTribute_GetTotalIncentives(t *testing.T) {
|
||||
c := newTributeClient(t)
|
||||
|
||||
// Initially should be 0
|
||||
c.Invoke(t, 0, "getTotalIncentives")
|
||||
}
|
||||
|
||||
// TestTribute_GetTotalRedistributions tests the getTotalRedistributions method.
|
||||
func TestTribute_GetTotalRedistributions(t *testing.T) {
|
||||
c := newTributeClient(t)
|
||||
|
||||
// Initially should be 0
|
||||
c.Invoke(t, 0, "getTotalRedistributions")
|
||||
}
|
||||
|
||||
// TestTribute_GetTributePool tests the getTributePool method.
|
||||
func TestTribute_GetTributePool(t *testing.T) {
|
||||
c := newTributeClient(t)
|
||||
|
||||
// Initially should be 0
|
||||
c.Invoke(t, 0, "getTributePool")
|
||||
}
|
||||
|
||||
// TestTribute_GetAccount_NonExistent tests getting a non-existent account.
|
||||
func TestTribute_GetAccount_NonExistent(t *testing.T) {
|
||||
c := newTributeClient(t)
|
||||
e := c.Executor
|
||||
|
||||
acc := e.NewAccount(t)
|
||||
|
||||
// Non-existent account 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 account")
|
||||
}, "getAccount", acc.ScriptHash())
|
||||
}
|
||||
|
||||
// TestTribute_GetVelocity_NonExistent tests getting velocity for non-existent account.
|
||||
func TestTribute_GetVelocity_NonExistent(t *testing.T) {
|
||||
c := newTributeClient(t)
|
||||
e := c.Executor
|
||||
|
||||
acc := e.NewAccount(t)
|
||||
|
||||
// Non-existent account should return 0 velocity
|
||||
c.Invoke(t, 0, "getVelocity", acc.ScriptHash())
|
||||
}
|
||||
|
||||
// TestTribute_GetHoardingLevel_NonExistent tests getting hoarding level for non-existent account.
|
||||
func TestTribute_GetHoardingLevel_NonExistent(t *testing.T) {
|
||||
c := newTributeClient(t)
|
||||
e := c.Executor
|
||||
|
||||
acc := e.NewAccount(t)
|
||||
|
||||
// Non-existent account should return 0 (HoardingNone)
|
||||
c.Invoke(t, 0, "getHoardingLevel", acc.ScriptHash())
|
||||
}
|
||||
|
||||
// TestTribute_IsExempt_NonExistent tests checking exemption for non-existent account.
|
||||
func TestTribute_IsExempt_NonExistent(t *testing.T) {
|
||||
c := newTributeClient(t)
|
||||
e := c.Executor
|
||||
|
||||
acc := e.NewAccount(t)
|
||||
|
||||
// Non-existent account should return false
|
||||
c.Invoke(t, false, "isExempt", acc.ScriptHash())
|
||||
}
|
||||
|
||||
// TestTribute_GetAssessment_NonExistent tests getting a non-existent assessment.
|
||||
func TestTribute_GetAssessment_NonExistent(t *testing.T) {
|
||||
c := newTributeClient(t)
|
||||
|
||||
// Non-existent assessment 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 assessment")
|
||||
}, "getAssessment", int64(999))
|
||||
}
|
||||
|
||||
// TestTribute_GetIncentive_NonExistent tests getting a non-existent incentive.
|
||||
func TestTribute_GetIncentive_NonExistent(t *testing.T) {
|
||||
c := newTributeClient(t)
|
||||
|
||||
// Non-existent incentive 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 incentive")
|
||||
}, "getIncentive", int64(999))
|
||||
}
|
||||
|
||||
// TestTribute_GetRedistribution_NonExistent tests getting a non-existent redistribution.
|
||||
func TestTribute_GetRedistribution_NonExistent(t *testing.T) {
|
||||
c := newTributeClient(t)
|
||||
|
||||
// Non-existent redistribution 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 redistribution")
|
||||
}, "getRedistribution", int64(999))
|
||||
}
|
||||
|
||||
// TestTribute_CreateVelocityAccount_NoVita tests that creating account without Vita fails.
|
||||
func TestTribute_CreateVelocityAccount_NoVita(t *testing.T) {
|
||||
c := newTributeClient(t)
|
||||
e := c.Executor
|
||||
|
||||
acc := e.NewAccount(t)
|
||||
invoker := c.WithSigners(acc)
|
||||
|
||||
// Should fail - no Vita registered
|
||||
invoker.InvokeFail(t, "owner must have an active Vita", "createVelocityAccount", acc.ScriptHash())
|
||||
}
|
||||
|
||||
// TestTribute_GrantExemption_NotAdmin tests that non-admin cannot grant exemption.
|
||||
func TestTribute_GrantExemption_NotAdmin(t *testing.T) {
|
||||
c := newTributeClient(t)
|
||||
e := c.Executor
|
||||
|
||||
acc := e.NewAccount(t)
|
||||
invoker := c.WithSigners(acc)
|
||||
|
||||
// Should fail - not admin
|
||||
invoker.InvokeFail(t, "caller is not an authorized tribute admin", "grantExemption",
|
||||
acc.ScriptHash(), "test exemption")
|
||||
}
|
||||
|
||||
// TestTribute_RevokeExemption_NotAdmin tests that non-admin cannot revoke exemption.
|
||||
func TestTribute_RevokeExemption_NotAdmin(t *testing.T) {
|
||||
c := newTributeClient(t)
|
||||
e := c.Executor
|
||||
|
||||
acc := e.NewAccount(t)
|
||||
invoker := c.WithSigners(acc)
|
||||
|
||||
// Should fail - not admin
|
||||
invoker.InvokeFail(t, "caller is not an authorized tribute admin", "revokeExemption", acc.ScriptHash())
|
||||
}
|
||||
|
||||
// TestTribute_GrantIncentive_NotAdmin tests that non-admin cannot grant incentive.
|
||||
func TestTribute_GrantIncentive_NotAdmin(t *testing.T) {
|
||||
c := newTributeClient(t)
|
||||
e := c.Executor
|
||||
|
||||
acc := e.NewAccount(t)
|
||||
invoker := c.WithSigners(acc)
|
||||
|
||||
// Should fail - not admin
|
||||
invoker.InvokeFail(t, "caller is not an authorized tribute admin", "grantIncentive",
|
||||
acc.ScriptHash(), int64(0), int64(1000), "test incentive")
|
||||
}
|
||||
|
||||
// TestTribute_WaiveTribute_NotAdmin tests that non-admin cannot waive tribute.
|
||||
func TestTribute_WaiveTribute_NotAdmin(t *testing.T) {
|
||||
c := newTributeClient(t)
|
||||
e := c.Executor
|
||||
|
||||
acc := e.NewAccount(t)
|
||||
invoker := c.WithSigners(acc)
|
||||
|
||||
// Should fail - not admin
|
||||
invoker.InvokeFail(t, "caller is not an authorized tribute admin", "waiveTribute",
|
||||
int64(0), "test waive")
|
||||
}
|
||||
|
||||
// TestTribute_Redistribute_NotCommittee tests that non-committee cannot redistribute.
|
||||
func TestTribute_Redistribute_NotCommittee(t *testing.T) {
|
||||
c := newTributeClient(t)
|
||||
e := c.Executor
|
||||
|
||||
acc := e.NewAccount(t)
|
||||
invoker := c.WithSigners(acc)
|
||||
|
||||
// Should fail - not committee
|
||||
invoker.InvokeFail(t, "invalid committee signature", "redistribute",
|
||||
"all_citizens", int64(100))
|
||||
}
|
||||
|
||||
// TestTribute_GetPendingAssessment_NoAccount tests getting pending assessment for non-existent account.
|
||||
func TestTribute_GetPendingAssessment_NoAccount(t *testing.T) {
|
||||
c := newTributeClient(t)
|
||||
e := c.Executor
|
||||
|
||||
acc := e.NewAccount(t)
|
||||
|
||||
// No account = 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 pending assessment")
|
||||
}, "getPendingAssessment", acc.ScriptHash())
|
||||
}
|
||||
|
||||
// TestTribute_GetUnclaimedIncentives_NonExistent tests getting unclaimed incentives for non-existent account.
|
||||
func TestTribute_GetUnclaimedIncentives_NonExistent(t *testing.T) {
|
||||
c := newTributeClient(t)
|
||||
e := c.Executor
|
||||
|
||||
acc := e.NewAccount(t)
|
||||
|
||||
// Non-existent account should return 0
|
||||
c.Invoke(t, 0, "getUnclaimedIncentives", acc.ScriptHash())
|
||||
}
|
||||
|
||||
// TestTribute_CreateVelocityAccountWithVita tests account creation with a valid Vita.
|
||||
func TestTribute_CreateVelocityAccountWithVita(t *testing.T) {
|
||||
c := newTributeClient(t)
|
||||
e := c.Executor
|
||||
|
||||
// Register Vita first
|
||||
vitaHash := e.NativeHash(t, nativenames.Vita)
|
||||
acc := e.NewAccount(t)
|
||||
vitaInvoker := e.NewInvoker(vitaHash, acc)
|
||||
|
||||
owner := acc.ScriptHash()
|
||||
personHash := hash.Sha256(owner.BytesBE()).BytesBE()
|
||||
isEntity := false
|
||||
recoveryHash := hash.Sha256([]byte("recovery")).BytesBE()
|
||||
|
||||
// Register Vita token
|
||||
vitaInvoker.InvokeAndCheck(t, func(t testing.TB, stack []stackitem.Item) {
|
||||
require.Equal(t, 1, len(stack))
|
||||
_, ok := stack[0].Value().([]byte)
|
||||
require.True(t, ok, "expected ByteArray result")
|
||||
}, "register", owner.BytesBE(), personHash, isEntity, recoveryHash)
|
||||
|
||||
// Now create Tribute account
|
||||
tributeInvoker := c.WithSigners(acc)
|
||||
tributeInvoker.Invoke(t, true, "createVelocityAccount", owner.BytesBE())
|
||||
|
||||
// Verify account exists
|
||||
tributeInvoker.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 for existing account")
|
||||
require.GreaterOrEqual(t, len(arr), 15) // VelocityAccount has 15 fields
|
||||
}, "getAccount", owner.BytesBE())
|
||||
|
||||
// Verify total accounts increased
|
||||
c.Invoke(t, 1, "getTotalAccounts")
|
||||
|
||||
// Verify default velocity (5000 = 50%)
|
||||
c.Invoke(t, 5000, "getVelocity", owner.BytesBE())
|
||||
|
||||
// Verify no hoarding (0 = HoardingNone)
|
||||
c.Invoke(t, 0, "getHoardingLevel", owner.BytesBE())
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,647 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/tutus-one/tutus-chain/pkg/util"
|
||||
"github.com/tutus-one/tutus-chain/pkg/vm/stackitem"
|
||||
)
|
||||
|
||||
// VelocityAccountStatus represents the velocity tracking account status.
|
||||
type VelocityAccountStatus uint8
|
||||
|
||||
const (
|
||||
// VelocityAccountActive indicates the account is actively tracked.
|
||||
VelocityAccountActive VelocityAccountStatus = 0
|
||||
// VelocityAccountExempt indicates the account is exempt from tribute.
|
||||
VelocityAccountExempt VelocityAccountStatus = 1
|
||||
// VelocityAccountSuspended indicates velocity tracking is suspended.
|
||||
VelocityAccountSuspended VelocityAccountStatus = 2
|
||||
)
|
||||
|
||||
// AssessmentStatus represents the status of a tribute assessment.
|
||||
type AssessmentStatus uint8
|
||||
|
||||
const (
|
||||
// AssessmentPending indicates the assessment is pending collection.
|
||||
AssessmentPending AssessmentStatus = 0
|
||||
// AssessmentCollected indicates the tribute has been collected.
|
||||
AssessmentCollected AssessmentStatus = 1
|
||||
// AssessmentWaived indicates the assessment was waived.
|
||||
AssessmentWaived AssessmentStatus = 2
|
||||
// AssessmentAppealed indicates the assessment is under appeal.
|
||||
AssessmentAppealed AssessmentStatus = 3
|
||||
)
|
||||
|
||||
// HoardingLevel represents the severity of resource hoarding.
|
||||
type HoardingLevel uint8
|
||||
|
||||
const (
|
||||
// HoardingNone indicates no hoarding detected.
|
||||
HoardingNone HoardingLevel = 0
|
||||
// HoardingMild indicates mild hoarding (below normal velocity).
|
||||
HoardingMild HoardingLevel = 1
|
||||
// HoardingModerate indicates moderate hoarding.
|
||||
HoardingModerate HoardingLevel = 2
|
||||
// HoardingSevere indicates severe hoarding.
|
||||
HoardingSevere HoardingLevel = 3
|
||||
// HoardingExtreme indicates extreme hoarding (stagnant resources).
|
||||
HoardingExtreme HoardingLevel = 4
|
||||
)
|
||||
|
||||
// IncentiveType represents types of circulation incentives.
|
||||
type IncentiveType uint8
|
||||
|
||||
const (
|
||||
// IncentiveVelocityBonus rewards high resource velocity.
|
||||
IncentiveVelocityBonus IncentiveType = 0
|
||||
// IncentiveProductiveUse rewards productive resource use.
|
||||
IncentiveProductiveUse IncentiveType = 1
|
||||
// IncentiveCommunitySupport rewards community contributions.
|
||||
IncentiveCommunitySupport IncentiveType = 2
|
||||
// IncentiveEducationSpending rewards education investment.
|
||||
IncentiveEducationSpending IncentiveType = 3
|
||||
// IncentiveHealthcareSpending rewards healthcare investment.
|
||||
IncentiveHealthcareSpending IncentiveType = 4
|
||||
)
|
||||
|
||||
// VelocityAccount tracks resource velocity for a Vita holder.
|
||||
type VelocityAccount struct {
|
||||
VitaID uint64 // Owner's Vita token ID
|
||||
Owner util.Uint160 // Owner's address
|
||||
CurrentVelocity uint64 // Current velocity score (0-10000 basis points)
|
||||
AverageVelocity uint64 // Rolling average velocity
|
||||
LastActivityBlock uint32 // Last transaction block
|
||||
TotalInflow uint64 // Total resources received
|
||||
TotalOutflow uint64 // Total resources spent/transferred
|
||||
StagnantBalance uint64 // Balance considered stagnant
|
||||
HoardingLevel HoardingLevel // Current hoarding assessment
|
||||
ExemptionReason string // Reason for exemption (if any)
|
||||
TotalTributePaid uint64 // Lifetime tribute paid
|
||||
TotalIncentivesRcvd uint64 // Lifetime incentives received
|
||||
Status VelocityAccountStatus // Account status
|
||||
CreatedAt uint32 // Block height when created
|
||||
UpdatedAt uint32 // Last update block
|
||||
}
|
||||
|
||||
// ToStackItem implements stackitem.Convertible.
|
||||
func (a *VelocityAccount) ToStackItem() stackitem.Item {
|
||||
return stackitem.NewArray([]stackitem.Item{
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(a.VitaID)),
|
||||
stackitem.NewByteArray(a.Owner.BytesBE()),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(a.CurrentVelocity)),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(a.AverageVelocity)),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.LastActivityBlock))),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(a.TotalInflow)),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(a.TotalOutflow)),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(a.StagnantBalance)),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.HoardingLevel))),
|
||||
stackitem.NewByteArray([]byte(a.ExemptionReason)),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(a.TotalTributePaid)),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(a.TotalIncentivesRcvd)),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.Status))),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.CreatedAt))),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.UpdatedAt))),
|
||||
})
|
||||
}
|
||||
|
||||
// FromStackItem implements stackitem.Convertible.
|
||||
func (a *VelocityAccount) FromStackItem(item stackitem.Item) error {
|
||||
arr, ok := item.Value().([]stackitem.Item)
|
||||
if !ok || len(arr) < 15 {
|
||||
return errors.New("not a struct")
|
||||
}
|
||||
vitaID, err := arr[0].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.VitaID = vitaID.Uint64()
|
||||
|
||||
ownerBytes, err := arr[1].TryBytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.Owner, err = util.Uint160DecodeBytesBE(ownerBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
currentVel, err := arr[2].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.CurrentVelocity = currentVel.Uint64()
|
||||
|
||||
avgVel, err := arr[3].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.AverageVelocity = avgVel.Uint64()
|
||||
|
||||
lastActivity, err := arr[4].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.LastActivityBlock = uint32(lastActivity.Uint64())
|
||||
|
||||
totalIn, err := arr[5].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.TotalInflow = totalIn.Uint64()
|
||||
|
||||
totalOut, err := arr[6].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.TotalOutflow = totalOut.Uint64()
|
||||
|
||||
stagnant, err := arr[7].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.StagnantBalance = stagnant.Uint64()
|
||||
|
||||
hoardLevel, err := arr[8].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.HoardingLevel = HoardingLevel(hoardLevel.Uint64())
|
||||
|
||||
exemptReason, err := arr[9].TryBytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.ExemptionReason = string(exemptReason)
|
||||
|
||||
totalTrib, err := arr[10].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.TotalTributePaid = totalTrib.Uint64()
|
||||
|
||||
totalInc, err := arr[11].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.TotalIncentivesRcvd = totalInc.Uint64()
|
||||
|
||||
status, err := arr[12].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.Status = VelocityAccountStatus(status.Uint64())
|
||||
|
||||
created, err := arr[13].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.CreatedAt = uint32(created.Uint64())
|
||||
|
||||
updated, err := arr[14].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.UpdatedAt = uint32(updated.Uint64())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TributeAssessment represents a hoarding tribute assessment.
|
||||
type TributeAssessment struct {
|
||||
ID uint64 // Unique assessment ID
|
||||
VitaID uint64 // Owner's Vita ID
|
||||
Owner util.Uint160 // Owner's address
|
||||
AssessmentBlock uint32 // Block when assessment made
|
||||
HoardingLevel HoardingLevel // Hoarding level at assessment
|
||||
StagnantAmount uint64 // Amount considered stagnant
|
||||
TributeRate uint64 // Rate applied (basis points)
|
||||
TributeAmount uint64 // Tribute amount due
|
||||
DueBlock uint32 // Block by which tribute is due
|
||||
CollectedBlock uint32 // Block when collected (0 if not)
|
||||
Status AssessmentStatus // Assessment status
|
||||
AppealReason string // Reason for appeal (if any)
|
||||
}
|
||||
|
||||
// ToStackItem implements stackitem.Convertible.
|
||||
func (a *TributeAssessment) ToStackItem() stackitem.Item {
|
||||
return stackitem.NewArray([]stackitem.Item{
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(a.ID)),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(a.VitaID)),
|
||||
stackitem.NewByteArray(a.Owner.BytesBE()),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.AssessmentBlock))),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.HoardingLevel))),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(a.StagnantAmount)),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(a.TributeRate)),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(a.TributeAmount)),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.DueBlock))),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.CollectedBlock))),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(a.Status))),
|
||||
stackitem.NewByteArray([]byte(a.AppealReason)),
|
||||
})
|
||||
}
|
||||
|
||||
// FromStackItem implements stackitem.Convertible.
|
||||
func (a *TributeAssessment) FromStackItem(item stackitem.Item) error {
|
||||
arr, ok := item.Value().([]stackitem.Item)
|
||||
if !ok || len(arr) < 12 {
|
||||
return errors.New("not a struct")
|
||||
}
|
||||
|
||||
id, err := arr[0].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.ID = id.Uint64()
|
||||
|
||||
vitaID, err := arr[1].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.VitaID = vitaID.Uint64()
|
||||
|
||||
ownerBytes, err := arr[2].TryBytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.Owner, err = util.Uint160DecodeBytesBE(ownerBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
assessBlock, err := arr[3].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.AssessmentBlock = uint32(assessBlock.Uint64())
|
||||
|
||||
hoardLevel, err := arr[4].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.HoardingLevel = HoardingLevel(hoardLevel.Uint64())
|
||||
|
||||
stagnant, err := arr[5].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.StagnantAmount = stagnant.Uint64()
|
||||
|
||||
rate, err := arr[6].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.TributeRate = rate.Uint64()
|
||||
|
||||
amount, err := arr[7].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.TributeAmount = amount.Uint64()
|
||||
|
||||
due, err := arr[8].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.DueBlock = uint32(due.Uint64())
|
||||
|
||||
collected, err := arr[9].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.CollectedBlock = uint32(collected.Uint64())
|
||||
|
||||
status, err := arr[10].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.Status = AssessmentStatus(status.Uint64())
|
||||
|
||||
appealReason, err := arr[11].TryBytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.AppealReason = string(appealReason)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CirculationIncentive represents a reward for resource circulation.
|
||||
type CirculationIncentive struct {
|
||||
ID uint64 // Unique incentive ID
|
||||
VitaID uint64 // Recipient's Vita ID
|
||||
Recipient util.Uint160 // Recipient's address
|
||||
IncentiveType IncentiveType // Type of incentive
|
||||
Amount uint64 // Incentive amount
|
||||
Reason string // Reason for incentive
|
||||
VelocityScore uint64 // Velocity score that triggered incentive
|
||||
GrantedBlock uint32 // Block when granted
|
||||
ClaimedBlock uint32 // Block when claimed (0 if not)
|
||||
Claimed bool // Whether incentive was claimed
|
||||
}
|
||||
|
||||
// ToStackItem implements stackitem.Convertible.
|
||||
func (i *CirculationIncentive) ToStackItem() stackitem.Item {
|
||||
return stackitem.NewArray([]stackitem.Item{
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(i.ID)),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(i.VitaID)),
|
||||
stackitem.NewByteArray(i.Recipient.BytesBE()),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(i.IncentiveType))),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(i.Amount)),
|
||||
stackitem.NewByteArray([]byte(i.Reason)),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(i.VelocityScore)),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(i.GrantedBlock))),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(i.ClaimedBlock))),
|
||||
stackitem.NewBool(i.Claimed),
|
||||
})
|
||||
}
|
||||
|
||||
// FromStackItem implements stackitem.Convertible.
|
||||
func (i *CirculationIncentive) FromStackItem(item stackitem.Item) error {
|
||||
arr, ok := item.Value().([]stackitem.Item)
|
||||
if !ok || len(arr) < 10 {
|
||||
return errors.New("not a struct")
|
||||
}
|
||||
|
||||
id, err := arr[0].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.ID = id.Uint64()
|
||||
|
||||
vitaID, err := arr[1].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.VitaID = vitaID.Uint64()
|
||||
|
||||
recipBytes, err := arr[2].TryBytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.Recipient, err = util.Uint160DecodeBytesBE(recipBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
incType, err := arr[3].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.IncentiveType = IncentiveType(incType.Uint64())
|
||||
|
||||
amount, err := arr[4].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.Amount = amount.Uint64()
|
||||
|
||||
reason, err := arr[5].TryBytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.Reason = string(reason)
|
||||
|
||||
velScore, err := arr[6].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.VelocityScore = velScore.Uint64()
|
||||
|
||||
granted, err := arr[7].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.GrantedBlock = uint32(granted.Uint64())
|
||||
|
||||
claimed, err := arr[8].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.ClaimedBlock = uint32(claimed.Uint64())
|
||||
|
||||
claimedBool, err := arr[9].TryBool()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.Claimed = claimedBool
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RedistributionRecord tracks wealth redistribution events.
|
||||
type RedistributionRecord struct {
|
||||
ID uint64 // Unique record ID
|
||||
SourceAssessment uint64 // Source assessment ID
|
||||
TotalAmount uint64 // Total amount redistributed
|
||||
RecipientCount uint64 // Number of recipients
|
||||
PerCapitaAmount uint64 // Amount per recipient
|
||||
RedistBlock uint32 // Block when redistribution occurred
|
||||
TargetCategory string // Category of recipients (e.g., "low_velocity", "all_citizens")
|
||||
}
|
||||
|
||||
// ToStackItem implements stackitem.Convertible.
|
||||
func (r *RedistributionRecord) ToStackItem() stackitem.Item {
|
||||
return stackitem.NewArray([]stackitem.Item{
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(r.ID)),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(r.SourceAssessment)),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(r.TotalAmount)),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(r.RecipientCount)),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(r.PerCapitaAmount)),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(r.RedistBlock))),
|
||||
stackitem.NewByteArray([]byte(r.TargetCategory)),
|
||||
})
|
||||
}
|
||||
|
||||
// FromStackItem implements stackitem.Convertible.
|
||||
func (r *RedistributionRecord) FromStackItem(item stackitem.Item) error {
|
||||
arr, ok := item.Value().([]stackitem.Item)
|
||||
if !ok || len(arr) < 7 {
|
||||
return errors.New("not a struct")
|
||||
}
|
||||
|
||||
id, err := arr[0].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.ID = id.Uint64()
|
||||
|
||||
sourceAssess, err := arr[1].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.SourceAssessment = sourceAssess.Uint64()
|
||||
|
||||
total, err := arr[2].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.TotalAmount = total.Uint64()
|
||||
|
||||
recipCount, err := arr[3].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.RecipientCount = recipCount.Uint64()
|
||||
|
||||
perCapita, err := arr[4].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.PerCapitaAmount = perCapita.Uint64()
|
||||
|
||||
redistBlock, err := arr[5].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.RedistBlock = uint32(redistBlock.Uint64())
|
||||
|
||||
targetCat, err := arr[6].TryBytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.TargetCategory = string(targetCat)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TributeConfig holds configurable parameters for the Tribute system.
|
||||
type TributeConfig struct {
|
||||
VelocityThresholdMild uint64 // Velocity below this = mild hoarding (basis points)
|
||||
VelocityThresholdModerate uint64 // Velocity below this = moderate hoarding
|
||||
VelocityThresholdSevere uint64 // Velocity below this = severe hoarding
|
||||
VelocityThresholdExtreme uint64 // Velocity below this = extreme hoarding
|
||||
TributeRateMild uint64 // Tribute rate for mild hoarding (basis points)
|
||||
TributeRateModerate uint64 // Tribute rate for moderate hoarding
|
||||
TributeRateSevere uint64 // Tribute rate for severe hoarding
|
||||
TributeRateExtreme uint64 // Tribute rate for extreme hoarding
|
||||
IncentiveRateHigh uint64 // Incentive rate for high velocity
|
||||
IncentiveRateVeryHigh uint64 // Incentive rate for very high velocity
|
||||
StagnancyPeriod uint32 // Blocks before balance considered stagnant
|
||||
AssessmentPeriod uint32 // Blocks between assessments
|
||||
GracePeriod uint32 // Blocks before tribute due after assessment
|
||||
MinBalanceForTribute uint64 // Minimum balance to assess tribute
|
||||
ExemptionThreshold uint64 // Balance below which exempt from tribute
|
||||
}
|
||||
|
||||
// ToStackItem implements stackitem.Convertible.
|
||||
func (c *TributeConfig) ToStackItem() stackitem.Item {
|
||||
return stackitem.NewArray([]stackitem.Item{
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(c.VelocityThresholdMild)),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(c.VelocityThresholdModerate)),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(c.VelocityThresholdSevere)),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(c.VelocityThresholdExtreme)),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(c.TributeRateMild)),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(c.TributeRateModerate)),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(c.TributeRateSevere)),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(c.TributeRateExtreme)),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(c.IncentiveRateHigh)),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(c.IncentiveRateVeryHigh)),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(c.StagnancyPeriod))),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(c.AssessmentPeriod))),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(uint64(c.GracePeriod))),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(c.MinBalanceForTribute)),
|
||||
stackitem.NewBigInteger(new(big.Int).SetUint64(c.ExemptionThreshold)),
|
||||
})
|
||||
}
|
||||
|
||||
// FromStackItem implements stackitem.Convertible.
|
||||
func (c *TributeConfig) FromStackItem(item stackitem.Item) error {
|
||||
arr, ok := item.Value().([]stackitem.Item)
|
||||
if !ok || len(arr) < 15 {
|
||||
return errors.New("not a struct")
|
||||
}
|
||||
|
||||
threshMild, err := arr[0].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.VelocityThresholdMild = threshMild.Uint64()
|
||||
|
||||
threshMod, err := arr[1].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.VelocityThresholdModerate = threshMod.Uint64()
|
||||
|
||||
threshSev, err := arr[2].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.VelocityThresholdSevere = threshSev.Uint64()
|
||||
|
||||
threshExt, err := arr[3].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.VelocityThresholdExtreme = threshExt.Uint64()
|
||||
|
||||
rateMild, err := arr[4].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.TributeRateMild = rateMild.Uint64()
|
||||
|
||||
rateMod, err := arr[5].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.TributeRateModerate = rateMod.Uint64()
|
||||
|
||||
rateSev, err := arr[6].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.TributeRateSevere = rateSev.Uint64()
|
||||
|
||||
rateExt, err := arr[7].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.TributeRateExtreme = rateExt.Uint64()
|
||||
|
||||
incHigh, err := arr[8].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.IncentiveRateHigh = incHigh.Uint64()
|
||||
|
||||
incVeryHigh, err := arr[9].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.IncentiveRateVeryHigh = incVeryHigh.Uint64()
|
||||
|
||||
stagnancy, err := arr[10].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.StagnancyPeriod = uint32(stagnancy.Uint64())
|
||||
|
||||
assessPeriod, err := arr[11].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.AssessmentPeriod = uint32(assessPeriod.Uint64())
|
||||
|
||||
grace, err := arr[12].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.GracePeriod = uint32(grace.Uint64())
|
||||
|
||||
minBal, err := arr[13].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.MinBalanceForTribute = minBal.Uint64()
|
||||
|
||||
exemptThresh, err := arr[14].TryInteger()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.ExemptionThreshold = exemptThresh.Uint64()
|
||||
|
||||
return nil
|
||||
}
|
||||
Loading…
Reference in New Issue