#define HAS_CALLBACK #include #include "hookapi.h" int64_t cbak(uint32_t reserved) { TRACESTR("Carbon: callback called."); return 0; } int64_t hook(uint32_t reserved) { TRACESTR("Carbon: started"); // before we start calling hook-api functions we should tell the hook how many tx we intend to create etxn_reserve(1); // we are going to emit 1 transaction // hooks communicate accounts via the 20 byte account ID, this can be generated from an raddr like so // a more efficient way to do this is precompute the account-id from the raddr (if the raddr never changes) uint8_t carbon_accid[20]; int64_t ret = util_accid( SBUF(carbon_accid), /* <-- generate into this buffer */ SBUF("rfCarbonVNTuXckX6x2qTMFmFSnm6dEWGX") ); /* <-- from this r-addr */ TRACEVAR(ret); // this api fetches the AccountID of the account the hook currently executing is installed on // since hooks can be triggered by both incoming and ougoing transactions this is important to know unsigned char hook_accid[20]; hook_account((uint32_t)hook_accid, 20); // NB: // almost all of the hook apis require a buffer pointer and buffer length to be supplied ... to make this a // little easier to code a macro: `SBUF(your_buffer)` expands to `your_buffer, sizeof(your_buffer)` // next fetch the sfAccount field from the originating transaction uint8_t account_field[20]; int32_t account_field_len = otxn_field(SBUF(account_field), sfAccount); TRACEVAR(account_field_len); if (account_field_len < 20) // negative values indicate errors from every api rollback(SBUF("Carbon: sfAccount field missing!!!"), 1); // this code could never be hit in prod // but it's here for completeness // compare the "From Account" (sfAccount) on the transaction with the account the hook is running on int equal = 0; BUFFER_EQUAL(equal, hook_accid, account_field, 20); if (!equal) { // if the accounts are not equal (memcmp != 0) the otxn was sent to the hook account by someone else // accept() it and end the hook execution here accept(SBUF("Carbon: Incoming transaction"), 2); } // execution to here means the user has sent a valid transaction FROM the account the hook is installed on // fetch the sent Amount // Amounts can be 384 bits or 64 bits. If the Amount is an XRP value it will be 64 bits. unsigned char amount_buffer[48]; int64_t amount_len = otxn_field(SBUF(amount_buffer), sfAmount); int64_t drops_to_send = 1000; // this will be the default if (amount_len != 8) { // you can trace the behaviour of your hook using the trace(buf, size, as_hex) api // which will output to xrpld's trace log TRACESTR("Carbon: Non-xrp transaction detected, sending default 1000 drops to rfCarbon"); } else { TRACESTR("Carbon: XRP transaction detected, computing 1% to send to rfCarbon"); int64_t otxn_drops = AMOUNT_TO_DROPS(amount_buffer); TRACEVAR(otxn_drops); if (otxn_drops > 100000) // if its less we send the default amount. or if there was an error we send default drops_to_send = (int64_t)((double)otxn_drops * 0.01f); // otherwise we send 1% } TRACEVAR(drops_to_send); // create a buffer to write the emitted transaction into unsigned char tx[PREPARE_PAYMENT_SIMPLE_SIZE]; // we will use an XRP payment macro, this will populate the buffer with a serialized binary transaction // Parameter list: ( buf_out, drops_amount, to_address, dest_tag, src_tag ) PREPARE_PAYMENT_SIMPLE(tx, drops_to_send, carbon_accid, 0, 0); // emit the transaction uint8_t emithash[32]; int64_t emit_result = emit(SBUF(emithash), SBUF(tx)); TRACEVAR(emit_result); // accept and allow the original transaction through accept(SBUF("Carbon: Emitted transaction"), 0); return 0; }