Skip to content

Instantly share code, notes, and snippets.

@KOSASIH
Created May 29, 2023 04:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save KOSASIH/145d083e3a8b6635b3cc53b69f317294 to your computer and use it in GitHub Desktop.
Save KOSASIH/145d083e3a8b6635b3cc53b69f317294 to your computer and use it in GitHub Desktop.

Peggy

Peggy hook provides an automated way to issue and redeem an over-collateralised XRP-backed stablecoin.

Sending XRP to it creates a vault and sends back stablecoin, which can be redeemed against the vault to get back the original XRP.

The exchange rate between XRP and stablecoin is the limit set on a trustline established between two special oracle accounts.

If the value of XRP falls too far, the vault gets under a minimum collateralization threshold. This means anyone who wants to top up the vault above the threshold can come along and take it over.

to test:

  • make sure Hooks Builder has at least 5 accounts: Alice, Bob, Carol, Carlos and Charlie
  • run decode.js, twice, to convert Carlos and Charlie accounts to binary form; save the values somewhere
  • if the Carlos account number is numerically higher than Charlie, switch the accounts (either re-import them, or just switch their names in the following text)
  • set up trust limit for the stablecoin user, by running trust-user.js; the script requires 2 parameters:
    1. the user account (Alice) sends the TrustSet transaction, so that the script requires its private key
    2. the hook account (Carol) is set up as the trusted issuer
  • set up trust limit on the oracle, by running trust-oracle.js; the script requires 2 parameters:
    1. the low account (Carlos) sends the TrustSet transaction, so that the script requires its private key
    2. the high account (Charlie) is set up as the trusted issuer
  • compile peggy.c and deploy it to Carol account, with 2 parameters: 1. "oracle_lo" set Carlos binary account 2. and "oracle_hi" set to the Charlie binary account
  • set up payment transaction from Alice to Carol
  • open debug stream filtered on Carol
  • run the transaction, see it succeed:
    • "Flow:TRC rippleCredit: Carol -> Alice : before=-66669133.31781069/USD/1 amount=2466.666666092349/USD/Carol after=-66671599.98447678/USD/1" in the debug stream
    • "[tesSUCCESS]" in the development log
const rac = require("ripple-address-codec");
/**
* @input {Account} a Account
*/
const a = process.env.a,
b = rac.decodeAccountID(a),
h = b.toString('hex').toUpperCase();
console.log(h);
// For documentation please see: https://xrpl-hooks.readme.io/reference/
// Generated using generate_error.sh
#ifndef HOOK_ERROR_CODES
#define SUCCESS 0
#define OUT_OF_BOUNDS -1
#define INTERNAL_ERROR -2
#define TOO_BIG -3
#define TOO_SMALL -4
#define DOESNT_EXIST -5
#define NO_FREE_SLOTS -6
#define INVALID_ARGUMENT -7
#define ALREADY_SET -8
#define PREREQUISITE_NOT_MET -9
#define FEE_TOO_LARGE -10
#define EMISSION_FAILURE -11
#define TOO_MANY_NONCES -12
#define TOO_MANY_EMITTED_TXN -13
#define NOT_IMPLEMENTED -14
#define INVALID_ACCOUNT -15
#define GUARD_VIOLATION -16
#define INVALID_FIELD -17
#define PARSE_ERROR -18
#define RC_ROLLBACK -19
#define RC_ACCEPT -20
#define NO_SUCH_KEYLET -21
#define NOT_AN_ARRAY -22
#define NOT_AN_OBJECT -23
#define INVALID_FLOAT -10024
#define DIVISION_BY_ZERO -25
#define MANTISSA_OVERSIZED -26
#define MANTISSA_UNDERSIZED -27
#define EXPONENT_OVERSIZED -28
#define EXPONENT_UNDERSIZED -29
#define OVERFLOW -30
#define NOT_IOU_AMOUNT -31
#define NOT_AN_AMOUNT -32
#define CANT_RETURN_NEGATIVE -33
#define NOT_AUTHORIZED -34
#define PREVIOUS_FAILURE_PREVENTS_RETRY -35
#define TOO_MANY_PARAMS -36
#define INVALID_TXN -37
#define RESERVE_INSUFFICIENT -38
#define COMPLEX_NOT_SUPPORTED -39
#define DOES_NOT_MATCH -40
#define HOOK_ERROR_CODES
#endif //HOOK_ERROR_CODES
// For documentation please see: https://xrpl-hooks.readme.io/reference/
// Generated using generate_extern.sh
#include <stdint.h>
#ifndef HOOK_EXTERN
extern int32_t
__attribute__((noduplicate))
_g(
uint32_t guard_id,
uint32_t maxiter
);
extern int64_t
accept(
uint32_t read_ptr,
uint32_t read_len,
int64_t error_code
);
extern int64_t
emit(
uint32_t write_ptr,
uint32_t write_len,
uint32_t read_ptr,
uint32_t read_len
);
extern int64_t
etxn_burden (
void
);
extern int64_t
etxn_details(
uint32_t write_ptr,
uint32_t write_len
);
extern int64_t
etxn_fee_base(
uint32_t read_ptr,
uint32_t read_len
);
extern int64_t
etxn_generation (
void
);
extern int64_t
etxn_nonce(
uint32_t write_ptr,
uint32_t write_len
);
extern int64_t
etxn_reserve(
uint32_t count
);
extern int64_t
fee_base (
void
);
extern int64_t
float_compare(
int64_t float1,
int64_t float2,
uint32_t mode
);
extern int64_t
float_divide(
int64_t float1,
int64_t float2
);
extern int64_t
float_exponent(
int64_t float1
);
extern int64_t
float_exponent_set(
int64_t float1,
int32_t exponent
);
extern int64_t
float_int(
int64_t float1,
uint32_t decimal_places,
uint32_t abs
);
extern int64_t
float_invert(
int64_t float1
);
extern int64_t
float_log(
int64_t float1
);
extern int64_t
float_mantissa(
int64_t float1
);
extern int64_t
float_mantissa_set(
int64_t float1,
int64_t mantissa
);
extern int64_t
float_mulratio(
int64_t float1,
uint32_t round_up,
uint32_t numerator,
uint32_t denominator
);
extern int64_t
float_multiply(
int64_t float1,
int64_t float2
);
extern int64_t
float_negate(
int64_t float1
);
extern int64_t
float_one (
void
);
extern int64_t
float_root(
int64_t float1,
uint32_t n
);
extern int64_t
float_set(
int32_t exponent,
int64_t mantissa
);
extern int64_t
float_sign(
int64_t float1
);
extern int64_t
float_sign_set(
int64_t float1,
uint32_t negative
);
extern int64_t
float_sto(
uint32_t write_ptr,
uint32_t write_len,
uint32_t cread_ptr,
uint32_t cread_len,
uint32_t iread_ptr,
uint32_t iread_len,
int64_t float1,
uint32_t field_code
);
extern int64_t
float_sto_set(
uint32_t read_ptr,
uint32_t read_len
);
extern int64_t
float_sum(
int64_t float1,
int64_t float2
);
extern int64_t
hook_account(
uint32_t write_ptr,
uint32_t write_len
);
extern int64_t
hook_again (
void
);
extern int64_t
hook_hash(
uint32_t write_ptr,
uint32_t write_len,
int32_t hook_no
);
extern int64_t
hook_param(
uint32_t write_ptr,
uint32_t write_len,
uint32_t read_ptr,
uint32_t read_len
);
extern int64_t
hook_param_set(
uint32_t read_ptr,
uint32_t read_len,
uint32_t kread_ptr,
uint32_t kread_len,
uint32_t hread_ptr,
uint32_t hread_len
);
extern int64_t
hook_pos (
void
);
extern int64_t
hook_skip(
uint32_t read_ptr,
uint32_t read_len,
uint32_t flags
);
extern int64_t
ledger_keylet(
uint32_t write_ptr,
uint32_t write_len,
uint32_t lread_ptr,
uint32_t lread_len,
uint32_t hread_ptr,
uint32_t hread_len
);
extern int64_t
ledger_last_hash(
uint32_t write_ptr,
uint32_t write_len
);
extern int64_t
ledger_last_time (
void
);
extern int64_t
ledger_nonce(
uint32_t write_ptr,
uint32_t write_len
);
extern int64_t
ledger_seq (
void
);
extern int64_t
meta_slot(
uint32_t slot_no
);
extern int64_t
otxn_burden (
void
);
extern int64_t
otxn_field(
uint32_t write_ptr,
uint32_t write_len,
uint32_t field_id
);
extern int64_t
otxn_field_txt(
uint32_t write_ptr,
uint32_t write_len,
uint32_t field_id
);
extern int64_t
otxn_generation (
void
);
extern int64_t
otxn_id(
uint32_t write_ptr,
uint32_t write_len,
uint32_t flags
);
extern int64_t
otxn_slot(
uint32_t slot_no
);
extern int64_t
otxn_type (
void
);
extern int64_t
rollback(
uint32_t read_ptr,
uint32_t read_len,
int64_t error_code
);
extern int64_t
slot(
uint32_t write_ptr,
uint32_t write_len,
uint32_t slot
);
extern int64_t
slot_clear(
uint32_t slot
);
extern int64_t
slot_count(
uint32_t slot
);
extern int64_t
slot_float(
uint32_t slot_no
);
extern int64_t
slot_id(
uint32_t write_ptr,
uint32_t write_len,
uint32_t slot
);
extern int64_t
slot_set(
uint32_t read_ptr,
uint32_t read_len,
int32_t slot
);
extern int64_t
slot_size(
uint32_t slot
);
extern int64_t
slot_subarray(
uint32_t parent_slot,
uint32_t array_id,
uint32_t new_slot
);
extern int64_t
slot_subfield(
uint32_t parent_slot,
uint32_t field_id,
uint32_t new_slot
);
extern int64_t
slot_type(
uint32_t slot_no,
uint32_t flags
);
extern int64_t
state(
uint32_t write_ptr,
uint32_t write_len,
uint32_t kread_ptr,
uint32_t kread_len
);
extern int64_t
state_foreign(
uint32_t write_ptr,
uint32_t write_len,
uint32_t kread_ptr,
uint32_t kread_len,
uint32_t nread_ptr,
uint32_t nread_len,
uint32_t aread_ptr,
uint32_t aread_len
);
extern int64_t
state_foreign_set(
uint32_t read_ptr,
uint32_t read_len,
uint32_t kread_ptr,
uint32_t kread_len,
uint32_t nread_ptr,
uint32_t nread_len,
uint32_t aread_ptr,
uint32_t aread_len
);
extern int64_t
state_set(
uint32_t read_ptr,
uint32_t read_len,
uint32_t kread_ptr,
uint32_t kread_len
);
extern int64_t
sto_emplace(
uint32_t write_ptr,
uint32_t write_len,
uint32_t sread_ptr,
uint32_t sread_len,
uint32_t fread_ptr,
uint32_t fread_len,
uint32_t field_id
);
extern int64_t
sto_erase(
uint32_t write_ptr,
uint32_t write_len,
uint32_t read_ptr,
uint32_t read_len,
uint32_t field_id
);
extern int64_t
sto_subarray(
uint32_t read_ptr,
uint32_t read_len,
uint32_t array_id
);
extern int64_t
sto_subfield(
uint32_t read_ptr,
uint32_t read_len,
uint32_t field_id
);
extern int64_t
sto_validate(
uint32_t tread_ptr,
uint32_t tread_len
);
extern int64_t
trace(
uint32_t mread_ptr,
uint32_t mread_len,
uint32_t dread_ptr,
uint32_t dread_len,
uint32_t as_hex
);
extern int64_t
trace_float(
uint32_t read_ptr,
uint32_t read_len,
int64_t float1
);
extern int64_t
trace_num(
uint32_t read_ptr,
uint32_t read_len,
int64_t number
);
extern int64_t
trace_slot(
uint32_t read_ptr,
uint32_t read_len,
uint32_t slot
);
extern int64_t
util_accid(
uint32_t write_ptr,
uint32_t write_len,
uint32_t read_ptr,
uint32_t read_len
);
extern int64_t
util_keylet(
uint32_t write_ptr,
uint32_t write_len,
uint32_t keylet_type,
uint32_t a,
uint32_t b,
uint32_t c,
uint32_t d,
uint32_t e,
uint32_t f
);
extern int64_t
util_raddr(
uint32_t write_ptr,
uint32_t write_len,
uint32_t read_ptr,
uint32_t read_len
);
extern int64_t
util_sha512h(
uint32_t write_ptr,
uint32_t write_len,
uint32_t read_ptr,
uint32_t read_len
);
extern int64_t
util_verify(
uint32_t dread_ptr,
uint32_t dread_len,
uint32_t sread_ptr,
uint32_t sread_len,
uint32_t kread_ptr,
uint32_t kread_len
);
#define HOOK_EXTERN
#endif //HOOK_EXTERN
/**
* Hook API include file
*
* Note to the reader:
* This include defines two types of things: external functions and macros
* Functions are used sparingly because a non-inlining compiler may produce
* undesirable output.
*
* Find documentation here: https://xrpl-hooks.readme.io/reference/
*/
#ifndef HOOKAPI_INCLUDED
#define HOOKAPI_INCLUDED 1
#define KEYLET_HOOK 1
#define KEYLET_HOOK_STATE 2
#define KEYLET_ACCOUNT 3
#define KEYLET_AMENDMENTS 4
#define KEYLET_CHILD 5
#define KEYLET_SKIP 6
#define KEYLET_FEES 7
#define KEYLET_NEGATIVE_UNL 8
#define KEYLET_LINE 9
#define KEYLET_OFFER 10
#define KEYLET_QUALITY 11
#define KEYLET_EMITTED_DIR 12
#define KEYLET_TICKET 13
#define KEYLET_SIGNERS 14
#define KEYLET_CHECK 15
#define KEYLET_DEPOSIT_PREAUTH 16
#define KEYLET_UNCHECKED 17
#define KEYLET_OWNER_DIR 18
#define KEYLET_PAGE 19
#define KEYLET_ESCROW 20
#define KEYLET_PAYCHAN 21
#define KEYLET_EMITTED 22
#define COMPARE_EQUAL 1U
#define COMPARE_LESS 2U
#define COMPARE_GREATER 4U
#include "error.h"
#include "extern.h"
#include "sfcodes.h"
#include "macro.h"
#endif
/**
* These are helper macros for writing hooks, all of them are optional as is including hookmacro.h at all
*/
#include <stdint.h>
#include "hookapi.h"
#include "sfcodes.h"
#ifndef HOOKMACROS_INCLUDED
#define HOOKMACROS_INCLUDED 1
#ifdef NDEBUG
#define DEBUG 0
#else
#define DEBUG 1
#endif
#define TRACEVAR(v) if (DEBUG) trace_num((uint32_t)(#v), (uint32_t)(sizeof(#v) - 1), (int64_t)v);
#define TRACEHEX(v) if (DEBUG) trace((uint32_t)(#v), (uint32_t)(sizeof(#v) - 1), (uint32_t)(v), (uint32_t)(sizeof(v)), 1);
#define TRACEXFL(v) if (DEBUG) trace_float((uint32_t)(#v), (uint32_t)(sizeof(#v) - 1), (int64_t)v);
#define TRACESTR(v) if (DEBUG) trace((uint32_t)(#v), (uint32_t)(sizeof(#v) - 1), (uint32_t)(v), sizeof(v), 0);
// hook developers should use this guard macro, simply GUARD(<maximum iterations>)
#define GUARD(maxiter) _g((1ULL << 31U) + __LINE__, (maxiter)+1)
#define GUARDM(maxiter, n) _g(( (1ULL << 31U) + (__LINE__ << 16) + n), (maxiter)+1)
#define SBUF(str) (uint32_t)(str), sizeof(str)
#define REQUIRE(cond, str)\
{\
if (!(cond))\
rollback(SBUF(str), __LINE__);\
}
// make a report buffer as a c-string
// provide a name for a buffer to declare (buf)
// provide a static string
// provide an integer to print after the string
#define RBUF(buf, out_len, str, num)\
unsigned char buf[sizeof(str) + 21];\
int out_len = 0;\
{\
int i = 0;\
for (; GUARDM(sizeof(str),1),i < sizeof(str); ++i)\
(buf)[i] = str[i];\
if ((buf)[sizeof(str)-1] == 0) i--;\
if ((num) < 0) (buf)[i++] = '-';\
uint64_t unsigned_num = (uint64_t)( (num) < 0 ? (num) * -1 : (num) );\
uint64_t j = 10000000000000000000ULL;\
int start = 1;\
for (; GUARDM(20,2), unsigned_num > 0 && j > 0; j /= 10)\
{\
unsigned char digit = ( unsigned_num / j ) % 10;\
if (digit == 0 && start)\
continue;\
start = 0;\
(buf)[i++] = '0' + digit;\
}\
(buf)[i] = '\0';\
out_len = i;\
}
#define RBUF2(buff, out_len, str, num, str2, num2)\
unsigned char buff[sizeof(str) + sizeof(str2) + 42];\
int out_len = 0;\
{\
unsigned char* __buf__ = buff;\
int i = 0;\
for (; GUARDM(sizeof(str),1),i < sizeof(str); ++i)\
(__buf__)[i] = str[i];\
if ((__buf__)[sizeof(str)-1] == 0) i--;\
if ((num) < 0) (__buf__)[i++] = '-';\
uint64_t unsigned_num = (uint64_t)( (num) < 0 ? (num) * -1 : (num) );\
uint64_t j = 10000000000000000000ULL;\
int start = 1;\
for (; GUARDM(20,2), unsigned_num > 0 && j > 0; j /= 10)\
{\
unsigned char digit = ( unsigned_num / j ) % 10;\
if (digit == 0 && start)\
continue;\
start = 0;\
(__buf__)[i++] = '0' + digit;\
}\
__buf__ += i;\
out_len += i;\
i = 0;\
for (; GUARDM(sizeof(str2),3),i < sizeof(str2); ++i)\
(__buf__)[i] = str2[i];\
if ((__buf__)[sizeof(str2)-1] == 0) i--;\
if ((num2) < 0) (__buf__)[i++] = '-';\
unsigned_num = (uint64_t)( (num2) < 0 ? (num2) * -1 : (num2) );\
j = 10000000000000000000ULL;\
start = 1;\
for (; GUARDM(20,4), unsigned_num > 0 && j > 0; j /= 10)\
{\
unsigned char digit = ( unsigned_num / j ) % 10;\
if (digit == 0 && start)\
continue;\
start = 0;\
(__buf__)[i++] = '0' + digit;\
}\
(__buf__)[i] = '\0';\
out_len += i;\
}
#define CLEARBUF(b)\
{\
for (int x = 0; GUARD(sizeof(b)), x < sizeof(b); ++x)\
b[x] = 0;\
}
// returns an in64_t, negative if error, non-negative if valid drops
#define AMOUNT_TO_DROPS(amount_buffer)\
(((amount_buffer)[0] >> 7) ? -2 : (\
((((uint64_t)((amount_buffer)[0])) & 0xb00111111) << 56) +\
(((uint64_t)((amount_buffer)[1])) << 48) +\
(((uint64_t)((amount_buffer)[2])) << 40) +\
(((uint64_t)((amount_buffer)[3])) << 32) +\
(((uint64_t)((amount_buffer)[4])) << 24) +\
(((uint64_t)((amount_buffer)[5])) << 16) +\
(((uint64_t)((amount_buffer)[6])) << 8) +\
(((uint64_t)((amount_buffer)[7])))))
#define SUB_OFFSET(x) ((int32_t)(x >> 32))
#define SUB_LENGTH(x) ((int32_t)(x & 0xFFFFFFFFULL))
// when using this macro buf1len may be dynamic but buf2len must be static
// provide n >= 1 to indicate how many times the macro will be hit on the line of code
// e.g. if it is in a loop that loops 10 times n = 10
#define BUFFER_EQUAL_GUARD(output, buf1, buf1len, buf2, buf2len, n)\
{\
output = ((buf1len) == (buf2len) ? 1 : 0);\
for (int x = 0; GUARDM( (buf2len) * (n), 1 ), output && x < (buf2len);\
++x)\
output = (buf1)[x] == (buf2)[x];\
}
#define BUFFER_SWAP(x,y)\
{\
uint8_t* __z__ = x;\
x = y;\
y = __z__;\
}
#define ACCOUNT_COMPARE(compare_result, buf1, buf2)\
{\
compare_result = 0;\
for (int i = 0; GUARD(20), i < 20; ++i)\
{\
if (buf1[i] > buf2[i])\
{\
compare_result = 1;\
break;\
}\
else if (buf1[i] < buf2[i])\
{\
compare_result = -1;\
break;\
}\
}\
}
#define BUFFER_EQUAL_STR_GUARD(output, buf1, buf1len, str, n)\
BUFFER_EQUAL_GUARD(output, buf1, buf1len, str, (sizeof(str)-1), n)
#define BUFFER_EQUAL_STR(output, buf1, buf1len, str)\
BUFFER_EQUAL_GUARD(output, buf1, buf1len, str, (sizeof(str)-1), 1)
#define BUFFER_EQUAL(output, buf1, buf2, compare_len)\
BUFFER_EQUAL_GUARD(output, buf1, compare_len, buf2, compare_len, 1)
#define UINT16_TO_BUF(buf_raw, i)\
{\
unsigned char* __buf__ = (unsigned char*)buf_raw;\
__buf__[0] = (((uint64_t)i) >> 8) & 0xFFUL;\
__buf__[1] = (((uint64_t)i) >> 0) & 0xFFUL;\
}
#define UINT16_FROM_BUF(buf)\
(((uint64_t)((buf)[0]) << 8) +\
((uint64_t)((buf)[1]) << 0))
#define UINT32_TO_BUF(buf_raw, i)\
{\
unsigned char* __buf__ = (unsigned char*)buf_raw;\
__buf__[0] = (((uint64_t)i) >> 24) & 0xFFUL;\
__buf__[1] = (((uint64_t)i) >> 16) & 0xFFUL;\
__buf__[2] = (((uint64_t)i) >> 8) & 0xFFUL;\
__buf__[3] = (((uint64_t)i) >> 0) & 0xFFUL;\
}
#define UINT32_FROM_BUF(buf)\
(((uint64_t)((buf)[0]) << 24) +\
((uint64_t)((buf)[1]) << 16) +\
((uint64_t)((buf)[2]) << 8) +\
((uint64_t)((buf)[3]) << 0))
#define UINT64_TO_BUF(buf_raw, i)\
{\
unsigned char* __buf__ = (unsigned char*)buf_raw;\
__buf__[0] = (((uint64_t)i) >> 56) & 0xFFUL;\
__buf__[1] = (((uint64_t)i) >> 48) & 0xFFUL;\
__buf__[2] = (((uint64_t)i) >> 40) & 0xFFUL;\
__buf__[3] = (((uint64_t)i) >> 32) & 0xFFUL;\
__buf__[4] = (((uint64_t)i) >> 24) & 0xFFUL;\
__buf__[5] = (((uint64_t)i) >> 16) & 0xFFUL;\
__buf__[6] = (((uint64_t)i) >> 8) & 0xFFUL;\
__buf__[7] = (((uint64_t)i) >> 0) & 0xFFUL;\
}
#define UINT64_FROM_BUF(buf)\
(((uint64_t)((buf)[0]) << 56) +\
((uint64_t)((buf)[1]) << 48) +\
((uint64_t)((buf)[2]) << 40) +\
((uint64_t)((buf)[3]) << 32) +\
((uint64_t)((buf)[4]) << 24) +\
((uint64_t)((buf)[5]) << 16) +\
((uint64_t)((buf)[6]) << 8) +\
((uint64_t)((buf)[7]) << 0))
#define INT64_FROM_BUF(buf)\
((((uint64_t)((buf)[0] & 0x7FU) << 56) +\
((uint64_t)((buf)[1]) << 48) +\
((uint64_t)((buf)[2]) << 40) +\
((uint64_t)((buf)[3]) << 32) +\
((uint64_t)((buf)[4]) << 24) +\
((uint64_t)((buf)[5]) << 16) +\
((uint64_t)((buf)[6]) << 8) +\
((uint64_t)((buf)[7]) << 0)) * (buf[0] & 0x80U ? -1 : 1))
#define INT64_TO_BUF(buf_raw, i)\
{\
unsigned char* __buf__ = (unsigned char*)buf_raw;\
__buf__[0] = (((uint64_t)i) >> 56) & 0x7FUL;\
__buf__[1] = (((uint64_t)i) >> 48) & 0xFFUL;\
__buf__[2] = (((uint64_t)i) >> 40) & 0xFFUL;\
__buf__[3] = (((uint64_t)i) >> 32) & 0xFFUL;\
__buf__[4] = (((uint64_t)i) >> 24) & 0xFFUL;\
__buf__[5] = (((uint64_t)i) >> 16) & 0xFFUL;\
__buf__[6] = (((uint64_t)i) >> 8) & 0xFFUL;\
__buf__[7] = (((uint64_t)i) >> 0) & 0xFFUL;\
if (i < 0) __buf__[0] |= 0x80U;\
}
#define ttPAYMENT 0
#define ttESCROW_CREATE 1
#define ttESCROW_FINISH 2
#define ttACCOUNT_SET 3
#define ttESCROW_CANCEL 4
#define ttREGULAR_KEY_SET 5
#define ttOFFER_CREATE 7
#define ttOFFER_CANCEL 8
#define ttTICKET_CREATE 10
#define ttSIGNER_LIST_SET 12
#define ttPAYCHAN_CREATE 13
#define ttPAYCHAN_FUND 14
#define ttPAYCHAN_CLAIM 15
#define ttCHECK_CREATE 16
#define ttCHECK_CASH 17
#define ttCHECK_CANCEL 18
#define ttDEPOSIT_PREAUTH 19
#define ttTRUST_SET 20
#define ttACCOUNT_DELETE 21
#define ttHOOK_SET 22
#define ttNFTOKEN_MINT 25
#define ttNFTOKEN_BURN 26
#define ttNFTOKEN_CREATE_OFFER 27
#define ttNFTOKEN_CANCEL_OFFER 28
#define ttNFTOKEN_ACCEPT_OFFER 29
#define ttURITOKEN_MINT 45
#define ttURITOKEN_BURN 46
#define ttURITOKEN_BUY 47
#define ttURITOKEN_CREATE_SELL_OFFER 48
#define ttURITOKEN_CANCEL_SELL_OFFER 49
#define ttCLAIM_REWARD 98
#define ttINVOKE 99
#define ttAMENDMENT 100
#define ttFEE 101
#define ttUNL_MODIFY 102
#define ttEMIT_FAILURE 103
#define tfCANONICAL 0x80000000UL
#define atACCOUNT 1U
#define atOWNER 2U
#define atDESTINATION 3U
#define atISSUER 4U
#define atAUTHORIZE 5U
#define atUNAUTHORIZE 6U
#define atTARGET 7U
#define atREGULARKEY 8U
#define atPSEUDOCALLBACK 9U
#define amAMOUNT 1U
#define amBALANCE 2U
#define amLIMITAMOUNT 3U
#define amTAKERPAYS 4U
#define amTAKERGETS 5U
#define amLOWLIMIT 6U
#define amHIGHLIMIT 7U
#define amFEE 8U
#define amSENDMAX 9U
#define amDELIVERMIN 10U
#define amMINIMUMOFFER 16U
#define amRIPPLEESCROW 17U
#define amDELIVEREDAMOUNT 18U
/**
* RH NOTE -- PAY ATTENTION
*
* ALL 'ENCODE' MACROS INCREMENT BUF_OUT
* THIS IS TO MAKE CHAINING EASY
* BUF_OUT IS A SACRIFICIAL POINTER
*
* 'ENCODE' MACROS WITH CONSTANTS HAVE
* ALIASING TO ASSIST YOU WITH ORDER
* _TYPECODE_FIELDCODE_ENCODE_MACRO
* TO PRODUCE A SERIALIZED OBJECT
* IN CANONICAL FORMAT YOU MUST ORDER
* FIRST BY TYPE CODE THEN BY FIELD CODE
*
* ALL 'PREPARE' MACROS PRESERVE POINTERS
*
**/
#define ENCODE_TL_SIZE 49
#define ENCODE_TL(buf_out, tlamt, amount_type)\
{\
uint8_t __uat__ = amount_type; \
buf_out[0] = 0x60U +(__uat__ & 0x0FU ); \
for (int i = 1; GUARDM(48, 1), i < 49; ++i)\
buf_out[i] = tlamt[i-1];\
buf_out += ENCODE_TL_SIZE;\
}
#define _06_XX_ENCODE_TL(buf_out, drops, amount_type )\
ENCODE_TL(buf_out, drops, amount_type );
#define ENCODE_TL_AMOUNT(buf_out, drops )\
ENCODE_TL(buf_out, drops, amAMOUNT );
#define _06_01_ENCODE_TL_AMOUNT(buf_out, drops )\
ENCODE_TL_AMOUNT(buf_out, drops );
// Encode drops to serialization format
// consumes 9 bytes
#define ENCODE_DROPS_SIZE 9
#define ENCODE_DROPS(buf_out, drops, amount_type ) \
{\
uint8_t __uat__ = amount_type; \
uint64_t __udrops__ = drops; \
buf_out[0] = 0x60U +(__uat__ & 0x0FU ); \
buf_out[1] = 0b01000000 + (( __udrops__ >> 56 ) & 0b00111111 ); \
buf_out[2] = (__udrops__ >> 48) & 0xFFU; \
buf_out[3] = (__udrops__ >> 40) & 0xFFU; \
buf_out[4] = (__udrops__ >> 32) & 0xFFU; \
buf_out[5] = (__udrops__ >> 24) & 0xFFU; \
buf_out[6] = (__udrops__ >> 16) & 0xFFU; \
buf_out[7] = (__udrops__ >> 8) & 0xFFU; \
buf_out[8] = (__udrops__ >> 0) & 0xFFU; \
buf_out += ENCODE_DROPS_SIZE; \
}
#define _06_XX_ENCODE_DROPS(buf_out, drops, amount_type )\
ENCODE_DROPS(buf_out, drops, amount_type );
#define ENCODE_DROPS_AMOUNT(buf_out, drops )\
ENCODE_DROPS(buf_out, drops, amAMOUNT );
#define _06_01_ENCODE_DROPS_AMOUNT(buf_out, drops )\
ENCODE_DROPS_AMOUNT(buf_out, drops );
#define ENCODE_DROPS_FEE(buf_out, drops )\
ENCODE_DROPS(buf_out, drops, amFEE );
#define _06_08_ENCODE_DROPS_FEE(buf_out, drops )\
ENCODE_DROPS_FEE(buf_out, drops );
#define ENCODE_TT_SIZE 3
#define ENCODE_TT(buf_out, tt )\
{\
uint8_t __utt__ = tt;\
buf_out[0] = 0x12U;\
buf_out[1] =(__utt__ >> 8 ) & 0xFFU;\
buf_out[2] =(__utt__ >> 0 ) & 0xFFU;\
buf_out += ENCODE_TT_SIZE; \
}
#define _01_02_ENCODE_TT(buf_out, tt)\
ENCODE_TT(buf_out, tt);
#define ENCODE_ACCOUNT_SIZE 22
#define ENCODE_ACCOUNT(buf_out, account_id, account_type)\
{\
uint8_t __uat__ = account_type;\
buf_out[0] = 0x80U + __uat__;\
buf_out[1] = 0x14U;\
*(uint64_t*)(buf_out + 2) = *(uint64_t*)(account_id + 0);\
*(uint64_t*)(buf_out + 10) = *(uint64_t*)(account_id + 8);\
*(uint32_t*)(buf_out + 18) = *(uint32_t*)(account_id + 16);\
buf_out += ENCODE_ACCOUNT_SIZE;\
}
#define _08_XX_ENCODE_ACCOUNT(buf_out, account_id, account_type)\
ENCODE_ACCOUNT(buf_out, account_id, account_type);
#define ENCODE_ACCOUNT_SRC_SIZE 22
#define ENCODE_ACCOUNT_SRC(buf_out, account_id)\
ENCODE_ACCOUNT(buf_out, account_id, atACCOUNT);
#define _08_01_ENCODE_ACCOUNT_SRC(buf_out, account_id)\
ENCODE_ACCOUNT_SRC(buf_out, account_id);
#define ENCODE_ACCOUNT_DST_SIZE 22
#define ENCODE_ACCOUNT_DST(buf_out, account_id)\
ENCODE_ACCOUNT(buf_out, account_id, atDESTINATION);
#define _08_03_ENCODE_ACCOUNT_DST(buf_out, account_id)\
ENCODE_ACCOUNT_DST(buf_out, account_id);
#define ENCODE_ACCOUNT_OWNER_SIZE 22
#define ENCODE_ACCOUNT_OWNER(buf_out, account_id) \
ENCODE_ACCOUNT(buf_out, account_id, atOWNER);
#define _08_02_ENCODE_ACCOUNT_OWNER(buf_out, account_id) \
ENCODE_ACCOUNT_OWNER(buf_out, account_id);
#define ENCODE_UINT32_COMMON_SIZE 5U
#define ENCODE_UINT32_COMMON(buf_out, i, field)\
{\
uint32_t __ui__ = i; \
uint8_t __uf__ = field; \
buf_out[0] = 0x20U +(__uf__ & 0x0FU); \
buf_out[1] =(__ui__ >> 24 ) & 0xFFU; \
buf_out[2] =(__ui__ >> 16 ) & 0xFFU; \
buf_out[3] =(__ui__ >> 8 ) & 0xFFU; \
buf_out[4] =(__ui__ >> 0 ) & 0xFFU; \
buf_out += ENCODE_UINT32_COMMON_SIZE; \
}
#define _02_XX_ENCODE_UINT32_COMMON(buf_out, i, field)\
ENCODE_UINT32_COMMON(buf_out, i, field)\
#define ENCODE_UINT32_UNCOMMON_SIZE 6U
#define ENCODE_UINT32_UNCOMMON(buf_out, i, field)\
{\
uint32_t __ui__ = i; \
uint8_t __uf__ = field; \
buf_out[0] = 0x20U; \
buf_out[1] = __uf__; \
buf_out[2] =(__ui__ >> 24 ) & 0xFFU; \
buf_out[3] =(__ui__ >> 16 ) & 0xFFU; \
buf_out[4] =(__ui__ >> 8 ) & 0xFFU; \
buf_out[5] =(__ui__ >> 0 ) & 0xFFU; \
buf_out += ENCODE_UINT32_UNCOMMON_SIZE; \
}
#define _02_XX_ENCODE_UINT32_UNCOMMON(buf_out, i, field)\
ENCODE_UINT32_UNCOMMON(buf_out, i, field)\
#define ENCODE_LLS_SIZE 6U
#define ENCODE_LLS(buf_out, lls )\
ENCODE_UINT32_UNCOMMON(buf_out, lls, 0x1B );
#define _02_27_ENCODE_LLS(buf_out, lls )\
ENCODE_LLS(buf_out, lls );
#define ENCODE_FLS_SIZE 6U
#define ENCODE_FLS(buf_out, fls )\
ENCODE_UINT32_UNCOMMON(buf_out, fls, 0x1A );
#define _02_26_ENCODE_FLS(buf_out, fls )\
ENCODE_FLS(buf_out, fls );
#define ENCODE_TAG_SRC_SIZE 5
#define ENCODE_TAG_SRC(buf_out, tag )\
ENCODE_UINT32_COMMON(buf_out, tag, 0x3U );
#define _02_03_ENCODE_TAG_SRC(buf_out, tag )\
ENCODE_TAG_SRC(buf_out, tag );
#define ENCODE_TAG_DST_SIZE 5
#define ENCODE_TAG_DST(buf_out, tag )\
ENCODE_UINT32_COMMON(buf_out, tag, 0xEU );
#define _02_14_ENCODE_TAG_DST(buf_out, tag )\
ENCODE_TAG_DST(buf_out, tag );
#define ENCODE_SEQUENCE_SIZE 5
#define ENCODE_SEQUENCE(buf_out, sequence )\
ENCODE_UINT32_COMMON(buf_out, sequence, 0x4U );
#define _02_04_ENCODE_SEQUENCE(buf_out, sequence )\
ENCODE_SEQUENCE(buf_out, sequence );
#define ENCODE_FLAGS_SIZE 5
#define ENCODE_FLAGS(buf_out, tag )\
ENCODE_UINT32_COMMON(buf_out, tag, 0x2U );
#define _02_02_ENCODE_FLAGS(buf_out, tag )\
ENCODE_FLAGS(buf_out, tag );
#define ENCODE_SIGNING_PUBKEY_SIZE 35
#define ENCODE_SIGNING_PUBKEY(buf_out, pkey )\
{\
buf_out[0] = 0x73U;\
buf_out[1] = 0x21U;\
*(uint64_t*)(buf_out + 2) = *(uint64_t*)(pkey + 0);\
*(uint64_t*)(buf_out + 10) = *(uint64_t*)(pkey + 8);\
*(uint64_t*)(buf_out + 18) = *(uint64_t*)(pkey + 16);\
*(uint64_t*)(buf_out + 26) = *(uint64_t*)(pkey + 24);\
buf[34] = pkey[32];\
buf_out += ENCODE_SIGNING_PUBKEY_SIZE;\
}
#define _07_03_ENCODE_SIGNING_PUBKEY(buf_out, pkey )\
ENCODE_SIGNING_PUBKEY(buf_out, pkey );
#define ENCODE_SIGNING_PUBKEY_NULL_SIZE 35
#define ENCODE_SIGNING_PUBKEY_NULL(buf_out )\
{\
buf_out[0] = 0x73U;\
buf_out[1] = 0x21U;\
*(uint64_t*)(buf_out+2) = 0;\
*(uint64_t*)(buf_out+10) = 0;\
*(uint64_t*)(buf_out+18) = 0;\
*(uint64_t*)(buf_out+25) = 0;\
buf_out += ENCODE_SIGNING_PUBKEY_NULL_SIZE;\
}
#define _07_03_ENCODE_SIGNING_PUBKEY_NULL(buf_out )\
ENCODE_SIGNING_PUBKEY_NULL(buf_out );
#ifdef HAS_CALLBACK
#define PREPARE_PAYMENT_SIMPLE_SIZE 270U
#else
#define PREPARE_PAYMENT_SIMPLE_SIZE 248U
#endif
#define PREPARE_PAYMENT_SIMPLE(buf_out_master, drops_amount_raw, to_address, dest_tag_raw, src_tag_raw)\
{\
uint8_t* __buf_out__ = buf_out_master;\
uint8_t __acc__[20];\
uint64_t __drops_amount__ = (drops_amount_raw);\
uint32_t __dest_tag__ = (dest_tag_raw);\
uint32_t __src_tag__ = (src_tag_raw);\
uint32_t __cls__ = (uint32_t)ledger_seq();\
hook_account(SBUF(__acc__));\
_01_02_ENCODE_TT (__buf_out__, ttPAYMENT ); /* uint16 | size 3 */ \
_02_02_ENCODE_FLAGS (__buf_out__, tfCANONICAL ); /* uint32 | size 5 */ \
_02_03_ENCODE_TAG_SRC (__buf_out__, __src_tag__ ); /* uint32 | size 5 */ \
_02_04_ENCODE_SEQUENCE (__buf_out__, 0 ); /* uint32 | size 5 */ \
_02_14_ENCODE_TAG_DST (__buf_out__, __dest_tag__ ); /* uint32 | size 5 */ \
_02_26_ENCODE_FLS (__buf_out__, __cls__ + 1 ); /* uint32 | size 6 */ \
_02_27_ENCODE_LLS (__buf_out__, __cls__ + 5 ); /* uint32 | size 6 */ \
_06_01_ENCODE_DROPS_AMOUNT (__buf_out__, __drops_amount__ ); /* amount | size 9 */ \
uint8_t* __fee_ptr__ = __buf_out__;\
_06_08_ENCODE_DROPS_FEE (__buf_out__, 0 ); /* amount | size 9 */ \
_07_03_ENCODE_SIGNING_PUBKEY_NULL (__buf_out__ ); /* pk | size 35 */ \
_08_01_ENCODE_ACCOUNT_SRC (__buf_out__, __acc__ ); /* account | size 22 */ \
_08_03_ENCODE_ACCOUNT_DST (__buf_out__, to_address ); /* account | size 22 */ \
int64_t __buf_size__ = PREPARE_PAYMENT_SIMPLE_SIZE - (__buf_out__ - buf_out_master); \
int64_t __edlen__ = etxn_details((uint32_t)__buf_out__, __buf_size__); /* emitdet | size 1?? */ \
int64_t __fee__ = etxn_fee_base(buf_out_master, PREPARE_PAYMENT_SIMPLE_SIZE); \
_06_08_ENCODE_DROPS_FEE (__fee_ptr__, __fee__ ); \
}
#ifdef HAS_CALLBACK
#define PREPARE_PAYMENT_SIMPLE_TRUSTLINE_SIZE 309
#else
#define PREPARE_PAYMENT_SIMPLE_TRUSTLINE_SIZE 288
#endif
#define PREPARE_PAYMENT_SIMPLE_TRUSTLINE(buf_out_master, tlamt, to_address, dest_tag_raw, src_tag_raw)\
{\
uint8_t* __buf_out__ = buf_out_master;\
uint8_t __acc__[20];\
uint32_t __dest_tag__ = (dest_tag_raw);\
uint32_t __src_tag__ = (src_tag_raw);\
uint32_t __cls__ = (uint32_t)ledger_seq();\
hook_account(SBUF(__acc__));\
_01_02_ENCODE_TT (__buf_out__, ttPAYMENT ); /* uint16 | size 3 */ \
_02_02_ENCODE_FLAGS (__buf_out__, tfCANONICAL ); /* uint32 | size 5 */ \
_02_03_ENCODE_TAG_SRC (__buf_out__, __src_tag__ ); /* uint32 | size 5 */ \
_02_04_ENCODE_SEQUENCE (__buf_out__, 0 ); /* uint32 | size 5 */ \
_02_14_ENCODE_TAG_DST (__buf_out__, __dest_tag__ ); /* uint32 | size 5 */ \
_02_26_ENCODE_FLS (__buf_out__, __cls__ + 1 ); /* uint32 | size 6 */ \
_02_27_ENCODE_LLS (__buf_out__, __cls__ + 5 ); /* uint32 | size 6 */ \
_06_01_ENCODE_TL_AMOUNT (__buf_out__, tlamt ); /* amount | size 48 */ \
uint8_t* __fee_ptr__ = __buf_out__;\
_06_08_ENCODE_DROPS_FEE (__buf_out__, 0 ); /* amount | size 9 */ \
_07_03_ENCODE_SIGNING_PUBKEY_NULL (__buf_out__ ); /* pk | size 35 */ \
_08_01_ENCODE_ACCOUNT_SRC (__buf_out__, __acc__ ); /* account | size 22 */ \
_08_03_ENCODE_ACCOUNT_DST (__buf_out__, to_address ); /* account | size 22 */ \
int64_t __buf_size__ = PREPARE_PAYMENT_SIMPLE_TRUSTLINE_SIZE - (__buf_out__ - buf_out_master); \
etxn_details((uint32_t)__buf_out__, __buf_size__); /* emitdet | size 1?? */ \
int64_t __fee__ = etxn_fee_base(buf_out_master, PREPARE_PAYMENT_SIMPLE_TRUSTLINE_SIZE); \
_06_08_ENCODE_DROPS_FEE (__fee_ptr__, __fee__ ); \
}
#endif
/**
* Peggy.c - An oracle based stable coin hook
*
* Author: KOSASIH
* Date: 29 May 2023
*
**/
#include <stdint.h>
#include "hookapi.h"
// your vault starts at 150% collateralization
#define NEW_COLLATERALIZATION_NUMERATOR 2
#define NEW_COLLATERALIZATION_DENOMINATOR 3
// at 120% collateralization your vault may be taken over
#define LIQ_COLLATERALIZATION_NUMERATOR 5
#define LIQ_COLLATERALIZATION_DENOMINATOR 6
int64_t hook(uint32_t reserved)
{
etxn_reserve(1);
uint8_t currency[20] = {0,0,0,0, 0,0,0,0, 0,0,0,0, 'U', 'S', 'D', 0,0,0,0,0};
// get the account the hook is running on and the account that created the txn
uint8_t hook_accid[20];
hook_account(SBUF(hook_accid));
uint8_t otxn_accid[20];
int32_t otxn_accid_len = otxn_field(SBUF(otxn_accid), sfAccount);
if (otxn_accid_len < 20)
rollback(SBUF("Peggy: sfAccount field missing!!!"), 10);
// get the source tag if any... negative means it wasn't provided
int64_t source_tag = otxn_field(0,0, sfSourceTag);
if (source_tag < 0)
source_tag = 0xFFFFFFFFU;
// compare the "From Account" (sfAccount) on the transaction with the account the hook is running on
int equal = 0; BUFFER_EQUAL(equal, hook_accid, otxn_accid, 20);
if (equal)
accept(SBUF("Peggy: Outgoing transaction"), 20);
// invoice id if present is used for taking over undercollateralized vaults
// format: { 20 byte account id | 4 byte tag [FFFFFFFFU if absent] | 8 bytes of 0 }
uint8_t invoice_id[32];
int64_t invoice_id_len = otxn_field(SBUF(invoice_id), sfInvoiceID);
// check if a trustline exists between the sender and the hook for the USD currency [ PUSD ]
uint8_t keylet[34];
if (util_keylet(SBUF(keylet), KEYLET_LINE, SBUF(hook_accid), SBUF(otxn_accid), SBUF(currency)) != 34)
rollback(SBUF("Peggy: Internal error, could not generate keylet"), 10);
int64_t user_peggy_trustline_slot = slot_set(SBUF(keylet), 0);
TRACEVAR(user_peggy_trustline_slot);
if (user_peggy_trustline_slot < 0)
rollback(SBUF("Peggy: You must have a trustline set for USD to this account."), 10);
// because the trustline is actually a ripplestate object with a 'high' and a 'low' account
// we need to compare the hook account with the user's account to determine which side of the line to
// examine for an adequate limit
int compare_result = 0;
ACCOUNT_COMPARE(compare_result, hook_accid, otxn_accid);
if (compare_result == 0)
rollback(SBUF("Peggy: Invalid trustline set hi=lo?"), 1);
int64_t lim_slot = slot_subfield(user_peggy_trustline_slot, ((compare_result > 0) ? sfLowLimit : sfHighLimit), 0);
if (lim_slot < 0)
rollback(SBUF("Peggy: Could not find sfLowLimit on oracle trustline"), 20);
int64_t user_trustline_limit = slot_float(lim_slot);
if (user_trustline_limit < 0)
rollback(SBUF("Peggy: Could not parse user trustline limit"), 1);
int64_t required_limit = float_set(10, 1);
if (float_compare(user_trustline_limit, required_limit, COMPARE_EQUAL | COMPARE_GREATER) != 1)
rollback(SBUF("Peggy: You must set a trustline for USD to peggy for limit of at least 10B"), 1);
// execution to here means the invoking account has the required trustline with the required limit
// now fetch the price oracle accounts and data (which also lives in a trustline)
uint8_t oracle_lo[32];
int64_t prv = hook_param(SBUF(oracle_lo), (uint32_t)"oracle_lo", 9);
if (prv < 20)
{
TRACEVAR(prv);
rollback(SBUF("Peggy: \"oracle_lo\" parameter missing"), 4);
}
uint8_t oracle_hi[32];
prv = hook_param(SBUF(oracle_hi), (uint32_t)"oracle_hi", 9);
if (prv < 20)
{
TRACEVAR(prv);
rollback(SBUF("Peggy: \"oracle_hi\" parameter missing"), 6);
}
if (util_keylet(SBUF(keylet), KEYLET_LINE, oracle_lo, 20, oracle_hi, 20, SBUF(currency)) != 34)
rollback(SBUF("Peggy: Internal error, could not generate keylet"), 10);
int64_t slot_no = slot_set(SBUF(keylet), 0);
TRACEVAR(slot_no);
if (slot_no < 0)
rollback(SBUF("Peggy: Could not find oracle trustline"), 10);
lim_slot = slot_subfield(slot_no, sfLowLimit, 0);
if (lim_slot < 0)
rollback(SBUF("Peggy: Could not find sfLowLimit on oracle trustline"), 20);
int64_t exchange_rate = slot_float(lim_slot);
if (exchange_rate < 0)
rollback(SBUF("Peggy: Could not get exchange rate float"), 20);
// execution to here means we have retrieved the exchange rate from the oracle
TRACEXFL(exchange_rate);
// process the amount sent, which could be either xrp or pusd
// to do this we 'slot' the originating txn, that is: we place it into a slot so we can use the slot api
// to examine its internals
int64_t oslot = otxn_slot(0);
if (oslot < 0)
rollback(SBUF("Peggy: Could not slot originating txn."), 1);
// specifically we're interested in the amount sent
int64_t amt_slot = slot_subfield(oslot, sfAmount, 0);
if (amt_slot < 0)
rollback(SBUF("Peggy: Could not slot otxn.sfAmount"), 2);
int64_t amt = slot_float(amt_slot);
if (amt < 0)
rollback(SBUF("Peggy: Could not parse amount."), 1);
// the slot_type api allows determination of fields and subtypes of fields according to the doco
// in this case we're examining an amount field to see if it's a native (xrp) amount or an iou amount
// this means passing flag=1
int64_t is_xrp = slot_type(amt_slot, 1);
if (is_xrp < 0)
rollback(SBUF("Peggy: Could not determine sent amount type"), 3);
// In addition to determining the amount sent (and its type) we also need to handle the "recollateralization"
// takeover mode. This is where another user, not the original vault owner, passes the vault ID as invoice ID
// and sends a payment that brings the vault back into a valid state. We then assign ownership to this person
// as a reward for stablising the vault. So account for this and record whether or not we are proceeding as
// the original vault owner (or a new vault) or in takeover mode.
uint8_t is_vault_owner = 1;
uint8_t vault_key[32] = { 0 };
if (invoice_id_len != 32)
{
// this is normal mode
for (int i = 0; GUARD(20), i < 20; ++i)
vault_key[i] = otxn_accid[i];
UINT32_TO_BUF(vault_key + 20, source_tag);
}
else
{
// this is the takeover mode
for (int i = 0; GUARD(24), i < 24; ++i)
vault_key[i] = invoice_id[i];
is_vault_owner = 0;
}
// check if state currently exists
uint8_t vault[16];
int64_t vault_pusd = 0;
int64_t vault_xrp = 0;
uint8_t vault_exists = 0;
if (state(SBUF(vault), SBUF(vault_key)) == 16)
{
vault_pusd = float_sto_set(vault, 8);
vault_xrp = float_sto_set(vault + 8, 8);
vault_exists = 1;
}
else if (is_vault_owner == 0)
rollback(SBUF("Peggy: You cannot takeover a vault that does not exist!"), 1);
if (is_xrp)
{
// XRP INCOMING
// decide whether the vault is liquidatable
int64_t required_vault_xrp = float_divide(vault_pusd, exchange_rate);
required_vault_xrp =
float_mulratio(required_vault_xrp, 0, LIQ_COLLATERALIZATION_DENOMINATOR, LIQ_COLLATERALIZATION_NUMERATOR);
uint8_t can_liq = (required_vault_xrp < vault_xrp);
// compute new vault xrp by adding the xrp they just sent
vault_xrp = float_sum(amt, vault_xrp);
// compute the maximum amount of pusd that can be out according to the collateralization
int64_t max_vault_pusd = float_multiply(vault_xrp, exchange_rate);
max_vault_pusd =
float_mulratio(max_vault_pusd, 0, NEW_COLLATERALIZATION_NUMERATOR, NEW_COLLATERALIZATION_DENOMINATOR);
// compute the amount we can send them
int64_t pusd_to_send =
float_sum(max_vault_pusd, float_negate(vault_pusd));
if (pusd_to_send < 0)
rollback(SBUF("Peggy: Error computing pusd to send"), 1);
// is the amount to send negative, that means the vault is undercollateralized
if (float_compare(pusd_to_send, 0, COMPARE_LESS))
{
if (!is_vault_owner)
rollback(SBUF("Peggy: Vault is undercollateralized and your deposit would not redeem it."), 1);
else
{
if (float_sto(vault + 8, 8, 0,0,0,0, vault_xrp, -1) != 8)
rollback(SBUF("Peggy: Internal error writing vault"), 1);
if (state_set(SBUF(vault), SBUF(vault_key)) != 16)
rollback(SBUF("Peggy: Could not set state"), 1);
accept(SBUF("Peggy: Vault is undercollateralized, absorbing without sending anything."), 0);
}
}
if (!is_vault_owner && !can_liq)
rollback(SBUF("Peggy: Vault is not sufficiently undercollateralized to take over yet."), 2);
// execution to here means we will send out pusd
// update the vault
vault_pusd = float_sum(vault_pusd, pusd_to_send);
// if this is a takeover we destroy the vault on the old key and recreate it on the new key
if (!is_vault_owner)
{
// destroy
if (state_set(0,0,SBUF(vault_key)) < 0)
rollback(SBUF("Peggy: Could not destroy old vault."), 1);
// reset the key
CLEARBUF(vault_key);
for (int i = 0; GUARD(20), i < 20; ++i)
vault_key[i] = otxn_accid[i];
vault_key[20] = (uint8_t)((source_tag >> 24U) & 0xFFU);
vault_key[21] = (uint8_t)((source_tag >> 16U) & 0xFFU);
vault_key[22] = (uint8_t)((source_tag >> 8U) & 0xFFU);
vault_key[23] = (uint8_t)((source_tag >> 0U) & 0xFFU);
}
// set / update the vault
if (float_sto(vault, 8, 0,0,0,0, vault_pusd, -1) != 8 ||
float_sto(vault + 8, 8, 0,0,0,0, vault_xrp, -1) != 8)
rollback(SBUF("Peggy: Internal error writing vault"), 1);
if (state_set(SBUF(vault), SBUF(vault_key)) != 16)
rollback(SBUF("Peggy: Could not set state"), 1);
// we need to dump the iou amount into a buffer
// by supplying -1 as the fieldcode we tell float_sto not to prefix an actual STO header on the field
uint8_t amt_out[48];
if (float_sto(SBUF(amt_out), 0, 0, 0, 0, pusd_to_send, -1) < 0)
rollback(SBUF("Peggy: Could not dump pusd amount into sto"), 1);
// set the currency code and issuer in the amount field
for (int i = 0; GUARD(20),i < 20; ++i)
{
amt_out[i + 28] = hook_accid[i];
amt_out[i + 8] = currency[i];
}
// finally create the outgoing txn
uint8_t txn_out[PREPARE_PAYMENT_SIMPLE_TRUSTLINE_SIZE];
PREPARE_PAYMENT_SIMPLE_TRUSTLINE(txn_out, amt_out, otxn_accid, source_tag, source_tag);
uint8_t emithash[32];
if (emit(SBUF(emithash), SBUF(txn_out)) < 0)
rollback(SBUF("Peggy: Emitting txn failed"), 1);
accept(SBUF("Peggy: Sent you PUSD!"), 0);
}
else
{
// NON-XRP incoming
if (!vault_exists)
rollback(SBUF("Peggy: Can only send PUSD back to an existing vault."), 1);
uint8_t amount_buffer[48];
if (slot(SBUF(amount_buffer), amt_slot) != 48)
rollback(SBUF("Peggy: Could not dump sfAmount"), 1);
// ensure the issuer is us
for (int i = 28; GUARD(20), i < 48; ++i)
{
if (amount_buffer[i] != hook_accid[i - 28])
rollback(SBUF("Peggy: A currency we didn't issue was sent to us."), 1);
}
// ensure the currency is PUSD
for (int i = 8; GUARD(20), i < 28; ++i)
{
if (amount_buffer[i] != currency[i - 8])
rollback(SBUF("Peggy: A non USD currency was sent to us."), 1);
}
TRACEVAR(vault_pusd);
// decide whether the vault is liquidatable
int64_t required_vault_xrp = float_divide(vault_pusd, exchange_rate);
required_vault_xrp =
float_mulratio(required_vault_xrp, 0, LIQ_COLLATERALIZATION_DENOMINATOR, LIQ_COLLATERALIZATION_NUMERATOR);
uint8_t can_liq = (required_vault_xrp < vault_xrp);
// compute new vault pusd by adding the pusd they just sent
vault_pusd = float_sum(float_negate(amt), vault_pusd);
// compute the maximum amount of pusd that can be out according to the collateralization
int64_t max_vault_xrp = float_divide(vault_pusd, exchange_rate);
max_vault_xrp =
float_mulratio(max_vault_xrp, 0, NEW_COLLATERALIZATION_DENOMINATOR, NEW_COLLATERALIZATION_NUMERATOR);
// compute the amount we can send them
int64_t xrp_to_send =
float_sum(float_negate(max_vault_xrp), vault_xrp);
if (xrp_to_send < 0)
rollback(SBUF("Peggy: Error computing xrp to send"), 1);
// is the amount to send negative, that means the vault is undercollateralized
if (float_compare(xrp_to_send, 0, COMPARE_LESS))
{
if (!is_vault_owner)
rollback(SBUF("Peggy: Vault is undercollateralized and your deposit would not redeem it."), 1);
else
{
if (float_sto(vault, 8, 0,0,0,0, vault_pusd, -1) != 8)
rollback(SBUF("Peggy: Internal error writing vault"), 1);
if (state_set(SBUF(vault), SBUF(vault_key)) != 16)
rollback(SBUF("Peggy: Could not set state"), 1);
accept(SBUF("Peggy: Vault is undercollateralized, absorbing without sending anything."), 0);
}
}
if (!is_vault_owner && !can_liq)
rollback(SBUF("Peggy: Vault is not sufficiently undercollateralized to take over yet."), 2);
// execution to here means we will send out pusd
// update the vault
vault_xrp = float_sum(vault_xrp, xrp_to_send);
// if this is a takeover we destroy the vault on the old key and recreate it on the new key
if (!is_vault_owner)
{
// destroy
if (state_set(0,0,SBUF(vault_key)) < 0)
rollback(SBUF("Peggy: Could not destroy old vault."), 1);
// reset the key
CLEARBUF(vault_key);
for (int i = 0; GUARD(20), i < 20; ++i)
vault_key[i] = otxn_accid[i];
vault_key[20] = (uint8_t)((source_tag >> 24U) & 0xFFU);
vault_key[21] = (uint8_t)((source_tag >> 16U) & 0xFFU);
vault_key[22] = (uint8_t)((source_tag >> 8U) & 0xFFU);
vault_key[23] = (uint8_t)((source_tag >> 0U) & 0xFFU);
}
// set / update the vault
if (float_sto(vault, 8, 0,0,0,0, vault_pusd, -1) != 8 ||
float_sto(vault + 8, 8, 0,0,0,0, max_vault_xrp, -1) != 8)
rollback(SBUF("Peggy: Internal error writing vault"), 1);
if (state_set(SBUF(vault), SBUF(vault_key)) != 16)
rollback(SBUF("Peggy: Could not set state"), 1);
// RH TODO: check the balance of the hook account
// finally create the outgoing txn
uint8_t txn_out[PREPARE_PAYMENT_SIMPLE_SIZE];
PREPARE_PAYMENT_SIMPLE(txn_out, float_int(xrp_to_send, 6, 0), otxn_accid, source_tag, source_tag);
uint8_t emithash[32];
if (emit(SBUF(emithash), SBUF(txn_out)) < 0)
rollback(SBUF("Peggy: Emitting txn failed"), 1);
accept(SBUF("Peggy: Sent you XRP!"), 0);
}
return 0;
}
// For documentation please see: https://xrpl-hooks.readme.io/reference/
// Generated using generate_sfcodes.sh
#define sfCloseResolution ((16U << 16U) + 1U)
#define sfMethod ((16U << 16U) + 2U)
#define sfTransactionResult ((16U << 16U) + 3U)
#define sfTickSize ((16U << 16U) + 16U)
#define sfUNLModifyDisabling ((16U << 16U) + 17U)
#define sfHookResult ((16U << 16U) + 18U)
#define sfLedgerEntryType ((1U << 16U) + 1U)
#define sfTransactionType ((1U << 16U) + 2U)
#define sfSignerWeight ((1U << 16U) + 3U)
#define sfTransferFee ((1U << 16U) + 4U)
#define sfVersion ((1U << 16U) + 16U)
#define sfHookStateChangeCount ((1U << 16U) + 17U)
#define sfHookEmitCount ((1U << 16U) + 18U)
#define sfHookExecutionIndex ((1U << 16U) + 19U)
#define sfHookApiVersion ((1U << 16U) + 20U)
#define sfNetworkID ((2U << 16U) + 1U)
#define sfFlags ((2U << 16U) + 2U)
#define sfSourceTag ((2U << 16U) + 3U)
#define sfSequence ((2U << 16U) + 4U)
#define sfPreviousTxnLgrSeq ((2U << 16U) + 5U)
#define sfLedgerSequence ((2U << 16U) + 6U)
#define sfCloseTime ((2U << 16U) + 7U)
#define sfParentCloseTime ((2U << 16U) + 8U)
#define sfSigningTime ((2U << 16U) + 9U)
#define sfExpiration ((2U << 16U) + 10U)
#define sfTransferRate ((2U << 16U) + 11U)
#define sfWalletSize ((2U << 16U) + 12U)
#define sfOwnerCount ((2U << 16U) + 13U)
#define sfDestinationTag ((2U << 16U) + 14U)
#define sfHighQualityIn ((2U << 16U) + 16U)
#define sfHighQualityOut ((2U << 16U) + 17U)
#define sfLowQualityIn ((2U << 16U) + 18U)
#define sfLowQualityOut ((2U << 16U) + 19U)
#define sfQualityIn ((2U << 16U) + 20U)
#define sfQualityOut ((2U << 16U) + 21U)
#define sfStampEscrow ((2U << 16U) + 22U)
#define sfBondAmount ((2U << 16U) + 23U)
#define sfLoadFee ((2U << 16U) + 24U)
#define sfOfferSequence ((2U << 16U) + 25U)
#define sfFirstLedgerSequence ((2U << 16U) + 26U)
#define sfLastLedgerSequence ((2U << 16U) + 27U)
#define sfTransactionIndex ((2U << 16U) + 28U)
#define sfOperationLimit ((2U << 16U) + 29U)
#define sfReferenceFeeUnits ((2U << 16U) + 30U)
#define sfReserveBase ((2U << 16U) + 31U)
#define sfReserveIncrement ((2U << 16U) + 32U)
#define sfSetFlag ((2U << 16U) + 33U)
#define sfClearFlag ((2U << 16U) + 34U)
#define sfSignerQuorum ((2U << 16U) + 35U)
#define sfCancelAfter ((2U << 16U) + 36U)
#define sfFinishAfter ((2U << 16U) + 37U)
#define sfSignerListID ((2U << 16U) + 38U)
#define sfSettleDelay ((2U << 16U) + 39U)
#define sfTicketCount ((2U << 16U) + 40U)
#define sfTicketSequence ((2U << 16U) + 41U)
#define sfNFTokenTaxon ((2U << 16U) + 42U)
#define sfMintedNFTokens ((2U << 16U) + 43U)
#define sfBurnedNFTokens ((2U << 16U) + 44U)
#define sfHookStateCount ((2U << 16U) + 45U)
#define sfEmitGeneration ((2U << 16U) + 46U)
#define sfLockCount ((2U << 16U) + 47U)
#define sfRewardTime ((2U << 16U) + 98U)
#define sfRewardLgrFirst ((2U << 16U) + 99U)
#define sfRewardLgrLast ((2U << 16U) + 100U)
#define sfIndexNext ((3U << 16U) + 1U)
#define sfIndexPrevious ((3U << 16U) + 2U)
#define sfBookNode ((3U << 16U) + 3U)
#define sfOwnerNode ((3U << 16U) + 4U)
#define sfBaseFee ((3U << 16U) + 5U)
#define sfExchangeRate ((3U << 16U) + 6U)
#define sfLowNode ((3U << 16U) + 7U)
#define sfHighNode ((3U << 16U) + 8U)
#define sfDestinationNode ((3U << 16U) + 9U)
#define sfCookie ((3U << 16U) + 10U)
#define sfServerVersion ((3U << 16U) + 11U)
#define sfNFTokenOfferNode ((3U << 16U) + 12U)
#define sfEmitBurden ((3U << 16U) + 13U)
#define sfHookInstructionCount ((3U << 16U) + 17U)
#define sfHookReturnCode ((3U << 16U) + 18U)
#define sfReferenceCount ((3U << 16U) + 19U)
#define sfRewardAccumulator ((3U << 16U) + 100U)
#define sfEmailHash ((4U << 16U) + 1U)
#define sfTakerPaysCurrency ((10U << 16U) + 1U)
#define sfTakerPaysIssuer ((10U << 16U) + 2U)
#define sfTakerGetsCurrency ((10U << 16U) + 3U)
#define sfTakerGetsIssuer ((10U << 16U) + 4U)
#define sfLedgerHash ((5U << 16U) + 1U)
#define sfParentHash ((5U << 16U) + 2U)
#define sfTransactionHash ((5U << 16U) + 3U)
#define sfAccountHash ((5U << 16U) + 4U)
#define sfPreviousTxnID ((5U << 16U) + 5U)
#define sfLedgerIndex ((5U << 16U) + 6U)
#define sfWalletLocator ((5U << 16U) + 7U)
#define sfRootIndex ((5U << 16U) + 8U)
#define sfAccountTxnID ((5U << 16U) + 9U)
#define sfNFTokenID ((5U << 16U) + 10U)
#define sfEmitParentTxnID ((5U << 16U) + 11U)
#define sfEmitNonce ((5U << 16U) + 12U)
#define sfEmitHookHash ((5U << 16U) + 13U)
#define sfBookDirectory ((5U << 16U) + 16U)
#define sfInvoiceID ((5U << 16U) + 17U)
#define sfNickname ((5U << 16U) + 18U)
#define sfAmendment ((5U << 16U) + 19U)
#define sfHookOn ((5U << 16U) + 20U)
#define sfDigest ((5U << 16U) + 21U)
#define sfChannel ((5U << 16U) + 22U)
#define sfConsensusHash ((5U << 16U) + 23U)
#define sfCheckID ((5U << 16U) + 24U)
#define sfValidatedHash ((5U << 16U) + 25U)
#define sfPreviousPageMin ((5U << 16U) + 26U)
#define sfNextPageMin ((5U << 16U) + 27U)
#define sfNFTokenBuyOffer ((5U << 16U) + 28U)
#define sfNFTokenSellOffer ((5U << 16U) + 29U)
#define sfHookStateKey ((5U << 16U) + 30U)
#define sfHookHash ((5U << 16U) + 31U)
#define sfHookNamespace ((5U << 16U) + 32U)
#define sfHookSetTxnID ((5U << 16U) + 33U)
#define sfOfferID ((5U << 16U) + 34U)
#define sfEscrowID ((5U << 16U) + 35U)
#define sfURITokenID ((5U << 16U) + 36U)
#define sfAmount ((6U << 16U) + 1U)
#define sfBalance ((6U << 16U) + 2U)
#define sfLimitAmount ((6U << 16U) + 3U)
#define sfTakerPays ((6U << 16U) + 4U)
#define sfTakerGets ((6U << 16U) + 5U)
#define sfLowLimit ((6U << 16U) + 6U)
#define sfHighLimit ((6U << 16U) + 7U)
#define sfFee ((6U << 16U) + 8U)
#define sfSendMax ((6U << 16U) + 9U)
#define sfDeliverMin ((6U << 16U) + 10U)
#define sfMinimumOffer ((6U << 16U) + 16U)
#define sfRippleEscrow ((6U << 16U) + 17U)
#define sfDeliveredAmount ((6U << 16U) + 18U)
#define sfNFTokenBrokerFee ((6U << 16U) + 19U)
#define sfHookCallbackFee ((6U << 16U) + 20U)
#define sfLockedBalance ((6U << 16U) + 21U)
#define sfPublicKey ((7U << 16U) + 1U)
#define sfMessageKey ((7U << 16U) + 2U)
#define sfSigningPubKey ((7U << 16U) + 3U)
#define sfTxnSignature ((7U << 16U) + 4U)
#define sfURI ((7U << 16U) + 5U)
#define sfSignature ((7U << 16U) + 6U)
#define sfDomain ((7U << 16U) + 7U)
#define sfFundCode ((7U << 16U) + 8U)
#define sfRemoveCode ((7U << 16U) + 9U)
#define sfExpireCode ((7U << 16U) + 10U)
#define sfCreateCode ((7U << 16U) + 11U)
#define sfMemoType ((7U << 16U) + 12U)
#define sfMemoData ((7U << 16U) + 13U)
#define sfMemoFormat ((7U << 16U) + 14U)
#define sfFulfillment ((7U << 16U) + 16U)
#define sfCondition ((7U << 16U) + 17U)
#define sfMasterSignature ((7U << 16U) + 18U)
#define sfUNLModifyValidator ((7U << 16U) + 19U)
#define sfValidatorToDisable ((7U << 16U) + 20U)
#define sfValidatorToReEnable ((7U << 16U) + 21U)
#define sfHookStateData ((7U << 16U) + 22U)
#define sfHookReturnString ((7U << 16U) + 23U)
#define sfHookParameterName ((7U << 16U) + 24U)
#define sfHookParameterValue ((7U << 16U) + 25U)
#define sfBlob ((7U << 16U) + 26U)
#define sfAccount ((8U << 16U) + 1U)
#define sfOwner ((8U << 16U) + 2U)
#define sfDestination ((8U << 16U) + 3U)
#define sfIssuer ((8U << 16U) + 4U)
#define sfAuthorize ((8U << 16U) + 5U)
#define sfUnauthorize ((8U << 16U) + 6U)
#define sfRegularKey ((8U << 16U) + 8U)
#define sfNFTokenMinter ((8U << 16U) + 9U)
#define sfEmitCallback ((8U << 16U) + 10U)
#define sfHookAccount ((8U << 16U) + 16U)
#define sfIndexes ((19U << 16U) + 1U)
#define sfHashes ((19U << 16U) + 2U)
#define sfAmendments ((19U << 16U) + 3U)
#define sfNFTokenOffers ((19U << 16U) + 4U)
#define sfHookNamespaces ((19U << 16U) + 5U)
#define sfPaths ((18U << 16U) + 1U)
#define sfTransactionMetaData ((14U << 16U) + 2U)
#define sfCreatedNode ((14U << 16U) + 3U)
#define sfDeletedNode ((14U << 16U) + 4U)
#define sfModifiedNode ((14U << 16U) + 5U)
#define sfPreviousFields ((14U << 16U) + 6U)
#define sfFinalFields ((14U << 16U) + 7U)
#define sfNewFields ((14U << 16U) + 8U)
#define sfTemplateEntry ((14U << 16U) + 9U)
#define sfMemo ((14U << 16U) + 10U)
#define sfSignerEntry ((14U << 16U) + 11U)
#define sfNFToken ((14U << 16U) + 12U)
#define sfEmitDetails ((14U << 16U) + 13U)
#define sfHook ((14U << 16U) + 14U)
#define sfSigner ((14U << 16U) + 16U)
#define sfMajority ((14U << 16U) + 18U)
#define sfDisabledValidator ((14U << 16U) + 19U)
#define sfEmittedTxn ((14U << 16U) + 20U)
#define sfHookExecution ((14U << 16U) + 21U)
#define sfHookDefinition ((14U << 16U) + 22U)
#define sfHookParameter ((14U << 16U) + 23U)
#define sfHookGrant ((14U << 16U) + 24U)
#define sfSigners ((15U << 16U) + 3U)
#define sfSignerEntries ((15U << 16U) + 4U)
#define sfTemplate ((15U << 16U) + 5U)
#define sfNecessary ((15U << 16U) + 6U)
#define sfSufficient ((15U << 16U) + 7U)
#define sfAffectedNodes ((15U << 16U) + 8U)
#define sfMemos ((15U << 16U) + 9U)
#define sfNFTokens ((15U << 16U) + 10U)
#define sfHooks ((15U << 16U) + 11U)
#define sfMajorities ((15U << 16U) + 16U)
#define sfDisabledValidators ((15U << 16U) + 17U)
#define sfHookExecutions ((15U << 16U) + 18U)
#define sfHookParameters ((15U << 16U) + 19U)
#define sfHookGrants ((15U << 16U) + 20U)
import { XrplClient } from "https://esm.sh/xrpl-client?bundle";
const lib = require("xrpl-accountlib");
const keypairs = require("ripple-keypairs");
/**
* @input {Account.secret} low_secret Low Account
* @input {Account} high_account High Account
*/
const { low_secret, high_account } = process.env
const low_keypair = lib.derive.familySeed(low_secret);
const low_account = keypairs.deriveAddress(low_keypair.keypair.publicKey);
console.log(low_account);
console.log(high_account);
const client = new XrplClient('wss://hooks-testnet-v3.xrpl-labs.com');
const main = async () => {
const { account_data } = await client.send({ command: 'account_info', 'account': low_account });
if (!account_data) {
console.log('Account not found.');
client.close();
return;
}
const tx = {
"TransactionType": "TrustSet",
"Account": low_account,
"Fee": "12",
"Flags": 262144,
"LimitAmount": {
"currency": "USD",
"issuer": high_account,
"value": "37" // $/XRP
},
"Sequence": account_data.Sequence,
"NetworkID": "21338"
};
const { signedTransaction } = lib.sign(tx, low_keypair);
const submit = await client.send({ command: 'submit', 'tx_blob': signedTransaction });
console.log(submit);
console.log('Shutting down...');
client.close();
};
main();
import { XrplClient } from "https://esm.sh/xrpl-client?bundle";
const lib = require("xrpl-accountlib");
const keypairs = require("ripple-keypairs");
/**
* @input {Account.secret} user_secret User Account
* @input {Account} hook_account Hook Account
*/
const { user_secret, hook_account } = process.env
const user_keypair = lib.derive.familySeed(user_secret);
const user_account = keypairs.deriveAddress(user_keypair.keypair.publicKey);
const client = new XrplClient('wss://hooks-testnet-v3.xrpl-labs.com');
const main = async () => {
const { account_data } = await client.send({ command: 'account_info', 'account': user_account });
if (!account_data) {
console.log('Account not found.');
client.close();
return;
}
const tx = {
"TransactionType": "TrustSet",
"Account": user_account,
"Fee": "12",
"Flags": 262144,
"LimitAmount": {
"currency": "USD",
"issuer": hook_account,
"value": "10000000000"
},
"Sequence": account_data.Sequence,
"NetworkID": "21338"
};
const { signedTransaction } = lib.sign(tx, user_keypair);
const submit = await client.send({ command: 'submit', 'tx_blob': signedTransaction });
console.log(submit);
console.log('Shutting down...');
client.close();
};
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment