Skip to content

Commit

Permalink
add display hook
Browse files Browse the repository at this point in the history
  • Loading branch information
dangell7 committed Nov 8, 2024
1 parent ea58acb commit 7e93b23
Show file tree
Hide file tree
Showing 3 changed files with 332 additions and 0 deletions.
135 changes: 135 additions & 0 deletions contracts-c/display/display.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
#include "hookapi.h"

#define SVAR(x) &(x), sizeof(x)

#define NOPE(x) \
{ \
return rollback((x), sizeof(x), __LINE__); \
}

#define MULTIPLIER 0.2 // 0.2 (6 XAH for 30 LEDGERS == 0.2 XAH PER LEDGER)
#define MAX 60 // 30 LEDGERS

uint8_t bids_ns[32] = {
0xAF, 0xD5, 0x20, 0x13, 0x12, 0xCC, 0xC1, 0xA5,
0x41, 0x02, 0x63, 0x25, 0x9D, 0x0B, 0x22, 0x30,
0x99, 0x18, 0x52, 0x16, 0x95, 0x5F, 0x8F, 0x6E,
0x1C, 0x71, 0xF4, 0x14, 0xF0, 0x32, 0x48, 0xB6};
;

int64_t hook(uint32_t r)
{
_g(1, 1);
// ACCOUNT: Origin Tx Account
uint8_t otxn_accid[32];
otxn_field(otxn_accid + 12, 20, sfAccount);

// ACCOUNT: Hook Account
uint8_t hook_accid[32];
hook_account(hook_accid + 12, 20);

// FILTER ON: ACCOUNT
if (BUFFER_EQUAL_20(hook_accid + 12, otxn_accid + 12))
DONE("display.c: outgoing tx on `Account`.");

int64_t tt = otxn_type();
if (tt != ttINVOKE && tt != ttPAYMENT)
NOPE("display.c: Rejecting non-Invoke, non-Payment txn.");

uint8_t amount_buffer[48];
otxn_slot(1);
slot_subfield(1, sfAmount, 2);

int64_t amount_xfl;
uint32_t flags;
if (tt == ttPAYMENT)
{
// this will fail if flags isn't in the txn, that's also ok.
otxn_field(&flags, 4, sfFlags);

// check for partial payments (0x00020000) -> (0x00000200 LE)
if (flags & 0x200U)
NOPE("display.c: Partial payments are not supported.");

otxn_field(SBUF(amount_buffer), sfAmount);

amount_xfl = slot_float(2);

if (amount_xfl < 0 || !float_compare(amount_xfl, 1, COMPARE_GREATER))
NOPE("display.c: Invalid sfAmount.");
}

// // Operation
uint8_t op;
if (otxn_param(&op, 1, "OP", 2) != 1)
NOPE("display.c: Missing OP parameter on Invoke/Payment.");

// sanity check
if (op == 'B' && tt != ttPAYMENT)
NOPE("display.c: Bid operations must be a payment transaction.");

// sanity check
if (op == 'U' && tt != ttINVOKE)
NOPE("display.c: Update operations must be a invoke transaction.");

int64_t count;
if (state(&count, 8, hook_accid + 12, 20) == DOESNT_EXIST)
{
// set current if initialization
ASSERT(0 < state_set(&count, 8, hook_accid + 12, 20));
}

switch (op)
{
case 'B':
{
uint8_t tid[40];
if (otxn_param(tid, 32, "ID", 2) != 32)
NOPE("display.c: Missing ID parameter on Payment.");

int64_t bid_count;
state_foreign(&bid_count, 8, hook_accid + 12, 20, bids_ns, 32, hook_accid + 12, 20);

int64_t end_ledger;
int64_t cur_ledger = ledger_seq();
if (state_foreign(&end_ledger, 8, hook_accid + 12, 20, 0, 32, hook_accid + 12, 20) == DOESNT_EXIST || end_ledger < cur_ledger)
{
end_ledger = cur_ledger;
}

// calculate ledgers
int64_t amount_int = float_int(amount_xfl, 0, 1);
int64_t duration = amount_int / MULTIPLIER;
if (duration > MAX)
NOPE("display.c: Duration cannot be more than 60 ledgers.");

end_ledger += duration;
UINT64_TO_BUF(tid + 32, end_ledger);
ASSERT(0 < state_foreign_set(tid, 40, &bid_count, 8, bids_ns, 32, hook_accid + 12, 20));

bid_count++;
ASSERT(0 < state_foreign_set(&bid_count, 8, hook_accid + 12, 20, bids_ns, 32, hook_accid + 12, 20));
ASSERT(0 < state_foreign_set(&end_ledger, 8, hook_accid + 12, 20, 0, 32, hook_accid + 12, 20));
TRACESTR("display.c: Bid.");
return accept(SBUF("display.c: Bid."), __LINE__);
}
case 'U':
{
uint8_t tid[40];
state_foreign(tid, 40, &count, 8, bids_ns, 32, hook_accid + 12, 20);
uint64_t final_ledger = UINT64_FROM_BUF(tid + 32);
int64_t cur_sequence = ledger_seq();
if (cur_sequence < final_ledger)
{
NOPE("display.c: Duration has not expired.");
}

state_foreign_set(0, 0, &count, 8, bids_ns, 32, hook_accid + 12, 20);

count++;
ASSERT(0 < state_set(&count, 8, hook_accid + 12, 20));
TRACESTR("display.c: Update.");
return accept(SBUF("display.c: Update."), __LINE__);
}
}
}
166 changes: 166 additions & 0 deletions test/integration-c/display/display.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
// xrpl
import {
URITokenMint,
Payment,
Invoke,
SetHookFlags,
convertStringToHex,
TransactionMetadata,
xrpToDrops,
} from '@transia/xrpl'
// xrpl-helpers
import {
XrplIntegrationTestContext,
setupClient,
teardownClient,
serverUrl,
close,
} from '@transia/hooks-toolkit/dist/npm/src/libs/xrpl-helpers'
// src
import {
SetHookParams,
createHookPayload,
setHooksV3,
iHookParamEntry,
iHookParamName,
iHookParamValue,
ExecutionUtility,
Xrpld,
StateUtility,
hexNamespace,
padHexString,
flipHex,
} from '@transia/hooks-toolkit/dist/npm/src'
import { hashURIToken } from '@transia/xrpl/dist/npm/utils/hashes'
import {
decodeModel,
hexToUInt64,
xrpAddressToHex,
} from '@transia/hooks-toolkit/dist/npm/src/libs/binary-models'
import { DisplayModel } from './models/DisplayModel'

// LevelThree: ACCEPT: success

describe('hunt', () => {
let testContext: XrplIntegrationTestContext

beforeAll(async () => {
testContext = await setupClient(serverUrl)
const hookWallet = testContext.hook1

const hook1 = createHookPayload({
version: 0,
createFile: 'display',
namespace: 'display',
flags: SetHookFlags.hsfOverride,
hookOnArray: ['Payment', 'Invoke'],
})
await setHooksV3({
client: testContext.client,
seed: hookWallet.seed,
hooks: [{ Hook: hook1 }],
} as SetHookParams)
})
afterAll(async () => teardownClient(testContext))

it('display - success', async () => {
const aliceWallet = testContext.alice
const hookWallet = testContext.hook1

// Bob: Mint and Sell
const random = Math.random()
const builtTx1: URITokenMint = {
TransactionType: 'URITokenMint',
Account: aliceWallet.classicAddress,
URI: convertStringToHex(`ipfs://${random}`),
}
await Xrpld.submit(testContext.client, {
wallet: aliceWallet,
tx: builtTx1,
})
await close(testContext.client)
const uriTokenID = hashURIToken(
aliceWallet.classicAddress,
`ipfs://${random}`
)

console.log(uriTokenID)

const param1 = new iHookParamEntry(
new iHookParamName('OP'),
new iHookParamValue('B')
)
const param2 = new iHookParamEntry(
new iHookParamName('ID'),
new iHookParamValue(
'35D1168C6D5B107A3B884B055D0657E36196882AAA16C68E345AC998717E86B3',
true
)
)
const builtTx: Payment = {
TransactionType: 'Payment',
Account: aliceWallet.classicAddress,
Destination: hookWallet.classicAddress,
Amount: xrpToDrops('6'),
HookParameters: [param1.toXrpl(), param2.toXrpl()],
}
const result = await Xrpld.submit(testContext.client, {
wallet: aliceWallet,
tx: builtTx,
})
const hookExecutions = await ExecutionUtility.getHookExecutionsFromMeta(
testContext.client,
result.meta as TransactionMetadata
)
console.log(hookExecutions.executions[0].HookReturnString)

await close(testContext.client)

const currentState = await StateUtility.getHookState(
testContext.client,
testContext.hook1.classicAddress,
padHexString(xrpAddressToHex(testContext.hook1.classicAddress)),
hexNamespace('display')
)

console.log(hexToUInt64(flipHex(currentState.HookStateData)))

console.log(padHexString(currentState.HookStateData, 64))
console.log(hexNamespace('bids'))

const state = await StateUtility.getHookState(
testContext.client,
testContext.hook1.classicAddress,
padHexString(currentState.HookStateData, 64),
hexNamespace('bids')
)

console.log(decodeModel(state.HookStateData, DisplayModel))

for (let index = 0; index < 30; index++) {
await close(testContext.client)
}

const tx2Param1 = new iHookParamEntry(
new iHookParamName('OP'),
new iHookParamValue('U')
)
const builtTx2: Invoke = {
TransactionType: 'Invoke',
Account: aliceWallet.classicAddress,
Destination: hookWallet.classicAddress,
HookParameters: [tx2Param1.toXrpl()],
}
const result2 = await Xrpld.submit(testContext.client, {
wallet: aliceWallet,
tx: builtTx2,
})
const hookExecutions2 = await ExecutionUtility.getHookExecutionsFromMeta(
testContext.client,
result2.meta as TransactionMetadata
)
console.log(hookExecutions2.executions[0].HookReturnString)

await close(testContext.client)
})
})
31 changes: 31 additions & 0 deletions test/integration-c/display/models/DisplayModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {
BaseModel,
Hash256,
Metadata,
UInt64,
} from '@transia/hooks-toolkit/dist/npm/src/libs/binary-models'

export class DisplayModel extends BaseModel {
id: Hash256
lastLedger: UInt64

constructor(id: Hash256, lastLedger: UInt64) {
super()
this.id = id
this.lastLedger = lastLedger
}

getMetadata(): Metadata {
return [
{ field: 'id', type: 'hash256' },
{ field: 'lastLedger', type: 'uint64' },
]
}

toJSON() {
return {
id: this.id,
lastLedger: this.lastLedger,
}
}
}

0 comments on commit 7e93b23

Please sign in to comment.