From 40fed20e684cf6dcc36e18ad4ee9fd1afd6630ec Mon Sep 17 00:00:00 2001 From: Denis Angell Date: Thu, 17 Oct 2024 14:14:14 +0200 Subject: [PATCH] treasury update --- contracts-c/treasury/genesis_mint.c | 71 ++++-------- contracts-c/treasury/treasury.c | 61 ++++++++++ .../treasury/genesisMint.test.ts | 104 ++++++++++++++++++ 3 files changed, 187 insertions(+), 49 deletions(-) create mode 100644 contracts-c/treasury/treasury.c create mode 100644 test/integration-c/treasury/genesisMint.test.ts diff --git a/contracts-c/treasury/genesis_mint.c b/contracts-c/treasury/genesis_mint.c index 87dbede..e6b3986 100644 --- a/contracts-c/treasury/genesis_mint.c +++ b/contracts-c/treasury/genesis_mint.c @@ -1,36 +1,25 @@ #include "hookapi.h" -#define DONE(x)\ - accept(SVAR(x),(uint32_t)__LINE__); - -#define NOPE(x)\ -{\ - return rollback((x), sizeof(x), __LINE__);\ -} - #define sfGenesisMints ((15U << 16U) + 96U) #define sfGenesisMint ((14U << 16U) + 96U) -#define ttGENESIS_MINT 96U -// clang-format off uint8_t txn[238] = { /* size,upto */ -/* 3, 0, tt = Payment */ 0x12U, 0x00U, 0x00U, -/* 5, 3, flags */ 0x22U, 0x00U, 0x00U, 0x00U, 0x00U, -/* 5, 8, sequence */ 0x24U, 0x00U, 0x00U, 0x00U, 0x00U, -/* 6, 13, firstledgersequence */ 0x20U, 0x1AU, 0x00U, 0x00U, 0x00U, 0x00U, -/* 6, 19, lastledgersequence */ 0x20U, 0x1BU, 0x00U, 0x00U, 0x00U, 0x00U, -/* 9, 25, amount */ 0x61U, 0x99U, 0x99U, 0x99U, 0x99U, 0x99U, 0x99U, 0x99U, 0x99U, -/* 9, 34, fee */ 0x68U, 0x40U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, -/* 35, 43, signingpubkey */ 0x73U, 0x21U, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, -/* 22, 78, account */ 0x81U, 0x14U, 0x88U, 0x59U, 0x00U, 0x4AU, 0xA9U, 0xEEU, 0xDCU, 0x00U, 0xB9U, 0xBDU, 0x5FU, 0xA0U, 0x9AU, 0xAEU, 0x43U, 0x25U, 0x52U, 0xECU, 0x84U, 0x3DU, -/* 22, 100, destination */ 0x83U, 0x14U, 0x88U, 0x58U, 0xFDU, 0x4DU, 0x78U, 0xCEU, 0x5EU, 0x97U, 0x2CU, 0x8CU, 0x1EU, 0x63U, 0xD5U, 0x1BU, 0x83U, 0x4FU, 0xF3U, 0x6FU, 0x14U, 0x11U, -/* 116, 122 emit details */ -/* 0, 238 */ +/* 3, 0, tt = Payment */ 0x12U, 0x00U, 0x00U, +/* 5, 3, flags */ 0x22U, 0x00U, 0x00U, 0x00U, 0x00U, +/* 5, 8, sequence */ 0x24U, 0x00U, 0x00U, 0x00U, 0x00U, +/* 6, 13, firstledgersequence */ 0x20U, 0x1AU, 0x00U, 0x00U, 0x00U, 0x00U, +/* 6, 19, lastledgersequence */ 0x20U, 0x1BU, 0x00U, 0x00U, 0x00U, 0x00U, +/* 9, 25, amount */ 0x61U, 0x99U, 0x99U, 0x99U, 0x99U, 0x99U, 0x99U, 0x99U, 0x99U, +/* 9, 34, fee */ 0x68U, 0x40U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, +/* 35, 43, signingpubkey */ 0x73U, 0x21U, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +/* 22, 78, account */ 0x81U, 0x14U, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +/* 22, 100, destination */ 0x83U, 0x14U, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +/* 116, 122 emit details */ +/* 0, 238 */ }; - #define FLS_OUT (txn + 15U) #define LLS_OUT (txn + 21U) #define FEE_OUT (txn + 35U) @@ -41,18 +30,11 @@ uint8_t txn[238] = int64_t hook(uint32_t reserved) { - _g(1,1); - hook_account(ACC_OUT, 20); - - hook_param(DEST_OUT, 20, "DST", 3); - - int64_t tt = otxn_type(); - if (tt != ttGENESIS_MINT) - DONE("genesis_mint.c: Pass Invalid Transaction Type."); + if(hook_param(DEST_OUT, 20, "D", 1) != 20) + rollback(SBUF("Genesis Mint: Destination Account not set as Hook parameter"), 1); otxn_slot(1); - slot_subfield(1, sfGenesisMints, 1); slot_subarray(1, 0, 1); slot_subfield(1, sfGenesisMint, 1); @@ -61,32 +43,24 @@ int64_t hook(uint32_t reserved) int64_t txn_amount = slot_float(2); uint8_t mint_dest[20]; - slot_subfield(1, sfDestination, 3); - slot(SBUF(mint_dest), 3); - - if (BUFFER_EQUAL_20(DEST_OUT, mint_dest)) - NOPE("genesis_mint.c: Invalid Mint Destination."); + slot_subfield(1, sfDestination, 1); + slot(SBUF(mint_dest), 1); - // if(slot_type(2, 1) != 0) - // NOPE("genesis_mint.c: Invalid Issued Currency."); + if (!BUFFER_EQUAL_20(ACC_OUT, mint_dest)) + rollback(SBUF("Genesis mint: Invalid Mint Destination."), 2); - // TXN: PREPARE: Init etxn_reserve(1); - // TXN PREPARE: FirstLedgerSequence uint32_t fls = (uint32_t)ledger_seq() + 1; *((uint32_t *)(FLS_OUT)) = FLIP_ENDIAN(fls); - // TXN PREPARE: LastLedgerSequense uint32_t lls = fls + 4; *((uint32_t *)(LLS_OUT)) = FLIP_ENDIAN(lls); - // TXN PREPARE: Emit Metadata + etxn_details(EMIT_OUT, 116U); - // TXN PREPARE: Amount uint64_t drops = float_int(txn_amount, 6, 1); - TRACEVAR(drops); uint8_t *b = AMOUNT_OUT + 1; *b++ = 0b01000000 + ((drops >> 56) & 0b00111111); *b++ = (drops >> 48) & 0xFFU; @@ -111,12 +85,11 @@ int64_t hook(uint32_t reserved) *b++ = (fee >> 0) & 0xFFU; } - TRACEHEX(txn); - uint8_t emithash[32]; if(emit(SBUF(emithash), SBUF(txn)) != 32) - rollback(SBUF("genesis_mint.c: Failed To Emit."), 9); + rollback(SBUF("Genesis Mint: Failed To Emit."), 3); - DONE("genesis_mint.c: Passing ClaimReward."); + accept(SBUF("Genesis Mint: Passing ClaimReward."), 4); + _g(1,1); return 0; } \ No newline at end of file diff --git a/contracts-c/treasury/treasury.c b/contracts-c/treasury/treasury.c new file mode 100644 index 0000000..4b79aec --- /dev/null +++ b/contracts-c/treasury/treasury.c @@ -0,0 +1,61 @@ +#include "hookapi.h" + +#define OTXN_AMT_TO_XFL(buf) float_set(-6, (AMOUNT_TO_DROPS(buf))) + +int64_t hook(uint32_t reserved) +{ + // ROLLBACK: Because not setting this would be a misconfiguration + uint64_t limit_amt; + if(hook_param(SVAR(limit_amt), "A", 1) != 8) + rollback(SBUF("Treasury: Misconfigured. Amount 'A' not set as Hook parameter"), 1); + + // ROLLBACK: Because not setting this would be a misconfiguration + int32_t limit_ledger; + if(hook_param(SVAR(limit_ledger), "L", 1) != 4) + rollback(SBUF("Treasury: Misconfigured. Ledger limit 'L' not set as Hook parameter"), 2); + + uint8_t otxn_account[20]; + otxn_field(SBUF(otxn_account), sfAccount); + + uint8_t account[20]; + hook_account(SBUF(otxn_account)); + if(!BUFFER_EQUAL_20(otxn_account, account)) + accept(SBUF("Treasury: Incoming Transaction."), 7); + + int64_t type = otxn_type(); + if (type == ttCLAIM_REWARD) + accept(SBUF("Treasury: ClaimReward Successful."), 4); + + // MUST ROLLBACK + uint8_t amount_buf[8]; + if(otxn_field(amount_buf, 8, sfAmount) != 8) + rollback(SBUF("Treasury: Non XAH currency payments are forbidden."), 6); + + int32_t last_release = 0; + state(SVAR(last_release), "LAST", 4); + + int32_t current_ledger = ledger_seq(); + + // MUST ROLLBACK + if ((last_release + limit_ledger) > current_ledger) + rollback(SBUF("Treasury: You need to wait longer to withdraw."), 8); + + if (last_release == 0 && type == ttSET_HOOK) + accept(SBUF("Treasury: Hook Set Successfully."), 13); + + // MUST ROLLBACK + if (type != ttPAYMENT) + rollback(SBUF("Treasury: Only ClaimReward and Payment txns are allowed."), 5); + + // MUST ROLLBACK + int64_t amount_xfl = OTXN_AMT_TO_XFL(amount_buf); + if(float_compare(amount_xfl, limit_amt, COMPARE_GREATER) == 1) + rollback(SBUF("Treasury: Outgoing transaction exceeds the limit set by you."), 9); + + if (state_set(SVAR(current_ledger), "LAST", 4) != 4) + rollback(SBUF("Treasury: Could not update state entry, bailing."), 11); + + accept(SBUF("Treasury: Released successfully."), 12); + _g(1,1); + return 0; +} \ No newline at end of file diff --git a/test/integration-c/treasury/genesisMint.test.ts b/test/integration-c/treasury/genesisMint.test.ts new file mode 100644 index 0000000..925e425 --- /dev/null +++ b/test/integration-c/treasury/genesisMint.test.ts @@ -0,0 +1,104 @@ +// xrpl +import { + Client, + Wallet, + AccountSetAsfFlags, + SetHookFlags, + TransactionMetadata, + ClaimReward, +} from '@transia/xrpl' +// xrpl-helpers +import { accountSet } from '@transia/hooks-toolkit/dist/npm/src/libs/xrpl-helpers' +// src +import { + Xrpld, + SetHookParams, + setHooksV3, + createHookPayload, + iHookParamEntry, + iHookParamName, + iHookParamValue, + ExecutionUtility, +} from '@transia/hooks-toolkit' +import { xrpAddressToHex } from '@transia/hooks-toolkit/dist/npm/src/libs/binary-models' + +const serverUrl = 'wss://xahau-test.net' +const hookSeed = 'snnf9az3KyNUfVRtER43grtixDrRy' +const destSeed = 'snDnCTwy5k2UdFq7fYnWxiXeEbdfJ' + +describe('treasury', () => { + let client: Client + + beforeAll(async () => { + client = new Client(serverUrl) + await client.connect() + client.networkID = await client.getNetworkID() + const hookWallet = Wallet.fromSeed(hookSeed) + const destWallet = Wallet.fromSeed(destSeed) + + accountSet(client, hookWallet, AccountSetAsfFlags.asfTshCollect) + const hookParam1 = new iHookParamEntry( + new iHookParamName('DST'), + new iHookParamValue(xrpAddressToHex(destWallet.classicAddress), true) + ) + const acct1hook1 = createHookPayload({ + version: 0, + createFile: 'genesis_mint', + namespace: 'treasury', + flags: SetHookFlags.hsfCollect + SetHookFlags.hsfOverride, + hookOnArray: ['GenesisMint'], + hookParams: [hookParam1.toXrpl()], + }) + await setHooksV3({ + client: client, + seed: hookWallet.seed, + hooks: [{ Hook: acct1hook1 }], + } as SetHookParams) + }) + + afterAll(async () => { + client.disconnect() + }) + + it('treasury - success', async () => { + client.networkID = await client.getNetworkID() + const hookWallet = Wallet.fromSeed(hookSeed) + + // Opt In ClaimReward + const optInTx: ClaimReward = { + TransactionType: 'ClaimReward', + Account: hookWallet.classicAddress, + Issuer: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', + } + await Xrpld.submit(client, { + wallet: hookWallet, + tx: optInTx, + debugStream: true, + }) + + // Wait 100 Seconds + await new Promise((resolve) => setTimeout(resolve, 100000)) // await + + // ClaimReward + const claimTx: ClaimReward = { + TransactionType: 'ClaimReward', + Account: hookWallet.classicAddress, + Issuer: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', + } + const result = await Xrpld.submit(client, { + wallet: hookWallet, + tx: claimTx, + debugStream: true, + }) + + const hookExecutions = await ExecutionUtility.getHookExecutionsFromMeta( + client, + result.meta as TransactionMetadata + ) + console.log(hookExecutions) + + expect(hookExecutions.executions[0].HookReturnString).toMatch( + 'auction_start.c: Tx emitted success' + ) + }) +})