Compare commits

...

22 Commits

Author SHA1 Message Date
Denis Angell
b054430406 Publish
- @transia/ripple-address-codec@4.2.7
 - @transia/ripple-binary-codec@1.4.5
 - @transia/ripple-keypairs@1.1.7
 - @transia/xrpl@2.7.2
2023-03-28 18:16:46 +00:00
Denis Angell
c29f512345 update readme links 2023-03-28 18:15:31 +00:00
Denis Angell
d86cd8d813 add public access 2023-03-28 17:51:21 +00:00
Denis Angell
40d9c2b1a1 update git head 2023-03-28 17:45:11 +00:00
Denis Angell
6c25d08019 Publish
- @transia/ripple-address-codec@4.2.6
 - @transia/ripple-binary-codec@1.4.4
 - @transia/ripple-keypairs@1.1.6
 - @transia/xrpl@2.7.1
2023-03-28 17:40:48 +00:00
Denis Angell
6f72189c13 update repo name 2023-03-28 17:39:37 +00:00
Denis Angell
5429930c0d update tests 2023-03-27 21:52:49 +00:00
Denis Angell
f82872fe9f add network id (req for sethook) 2023-03-27 21:48:58 +00:00
Denis Angell
8d16592abb add uritoken (req for hook on calc) 2023-03-27 21:48:43 +00:00
Denis Angell
f80d6e1747 update hook on calcaulator 2023-03-27 21:47:36 +00:00
Denis Angell
df1897be10 fixup unit test 2023-03-27 19:04:31 +00:00
Denis Angell
bcbba483c5 fix error 2023-03-27 18:58:49 +00:00
Denis Angell
0560a5867f lint 2023-03-27 18:57:11 +00:00
Denis Angell
f19466cabe export transaction type map from definitions 2023-03-27 18:31:22 +00:00
Denis Angell
f6a90a31c4 remove network id 2023-03-27 18:30:52 +00:00
Denis Angell
818d12d68c update definitions 2023-03-27 17:45:08 +00:00
Denis Angell
277288f1e9 remove sugar export 2023-03-27 17:17:56 +00:00
Denis Angell
ee59c64b65 ledger entry objects 2023-03-27 17:14:43 +00:00
Denis Angell
d36349e410 add invoke tx 2023-03-27 17:14:33 +00:00
Denis Angell
2e38cf54a8 update hookon transaction types 2023-03-27 17:14:15 +00:00
Denis Angell
3a2a0918f8 update hook
update hook

add test and validation

add estimate fee func

fix estimated fee error

add hook utils
2023-03-27 17:10:35 +00:00
Denis Angell
6d6b5a5863 feature Hooks
update definitions

add network id to base transaction

add set hook

update required

update definitions

make network id required
2023-03-27 17:10:26 +00:00
31 changed files with 1016 additions and 36 deletions

92
package-lock.json generated
View File

@@ -2722,6 +2722,22 @@
"node": ">= 6"
}
},
"node_modules/@transia/ripple-address-codec": {
"resolved": "packages/ripple-address-codec",
"link": true
},
"node_modules/@transia/ripple-binary-codec": {
"resolved": "packages/ripple-binary-codec",
"link": true
},
"node_modules/@transia/ripple-keypairs": {
"resolved": "packages/ripple-keypairs",
"link": true
},
"node_modules/@transia/xrpl": {
"resolved": "packages/xrpl",
"link": true
},
"node_modules/@tsconfig/node10": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
@@ -17124,7 +17140,8 @@
}
},
"packages/ripple-address-codec": {
"version": "4.2.5",
"name": "@transia/ripple-address-codec",
"version": "4.2.7",
"license": "ISC",
"dependencies": {
"base-x": "^3.0.9",
@@ -17135,7 +17152,8 @@
}
},
"packages/ripple-binary-codec": {
"version": "1.4.3",
"name": "@transia/ripple-binary-codec",
"version": "1.4.5",
"license": "ISC",
"dependencies": {
"assert": "^2.0.0",
@@ -17158,7 +17176,8 @@
}
},
"packages/ripple-keypairs": {
"version": "1.1.5",
"name": "@transia/ripple-keypairs",
"version": "1.1.7",
"license": "ISC",
"dependencies": {
"bn.js": "^5.1.1",
@@ -17172,7 +17191,8 @@
}
},
"packages/xrpl": {
"version": "2.7.0",
"name": "@transia/xrpl",
"version": "2.7.2",
"license": "ISC",
"dependencies": {
"bignumber.js": "^9.0.0",
@@ -19350,6 +19370,70 @@
"integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
"dev": true
},
"@transia/ripple-address-codec": {
"version": "file:packages/ripple-address-codec",
"requires": {
"base-x": "^3.0.9",
"create-hash": "^1.1.2"
}
},
"@transia/ripple-binary-codec": {
"version": "file:packages/ripple-binary-codec",
"requires": {
"assert": "^2.0.0",
"big-integer": "^1.6.48",
"buffer": "5.6.0",
"create-hash": "^1.2.0",
"decimal.js": "^10.2.0",
"ripple-address-codec": "^4.2.5"
},
"dependencies": {
"buffer": {
"version": "5.6.0",
"requires": {
"base64-js": "^1.0.2",
"ieee754": "^1.1.4"
}
}
}
},
"@transia/ripple-keypairs": {
"version": "file:packages/ripple-keypairs",
"requires": {
"bn.js": "^5.1.1",
"brorand": "^1.0.5",
"elliptic": "^6.5.4",
"hash.js": "^1.0.3",
"ripple-address-codec": "^4.2.5"
}
},
"@transia/xrpl": {
"version": "file:packages/xrpl",
"requires": {
"@geut/browser-node-core": "^2.0.13",
"@types/node": "^14.18.36",
"assert-browserify": "^2.0.0",
"bignumber.js": "^9.0.0",
"bip32": "^2.0.6",
"bip39": "^3.0.4",
"browserify-fs": "^1.0.0",
"constants-browserify": "^1.0.0",
"https-browserify": "^1.0.0",
"https-proxy-agent": "^5.0.0",
"karma": "^6.4.1",
"karma-chrome-launcher": "^3.1.1",
"karma-jasmine": "^5.1.0",
"karma-webpack": "^5.0.0",
"lodash": "^4.17.4",
"node-polyfill-webpack-plugin": "^2.0.1",
"react": "^18.2.0",
"ripple-address-codec": "^4.2.5",
"ripple-binary-codec": "^1.4.3",
"ripple-keypairs": "^1.1.5",
"typedoc": "^0.23.24",
"ws": "^8.2.2"
}
},
"@tsconfig/node10": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",

View File

@@ -1,12 +1,12 @@
{
"name": "ripple-address-codec",
"version": "4.2.5",
"version": "4.2.7",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "ripple-address-codec",
"version": "4.2.4",
"version": "4.2.7",
"license": "ISC",
"dependencies": {
"base-x": "^3.0.9",

View File

@@ -1,6 +1,6 @@
{
"name": "ripple-address-codec",
"version": "4.2.5",
"name": "@transia/ripple-address-codec",
"version": "4.2.7",
"description": "encodes/decodes base58 encoded XRP Ledger identifiers",
"files": [
"dist/*",
@@ -15,7 +15,7 @@
},
"repository": {
"type": "git",
"url": "git@github.com:XRPLF/xrpl.js.git"
"url": "git@github.com:Transia-RnD/xrpl.js.git"
},
"prepublish": "tsc -b",
"prepublishOnly": "tslint -b ./ && jest",
@@ -28,5 +28,9 @@
"prettier": "@xrplf/prettier-config",
"engines": {
"node": ">= 10"
},
"gitHead": "40d9c2b1a158ab480df7e8b1c7dc2ed3b88afc75",
"publishConfig": {
"access": "public"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "ripple-binary-codec",
"version": "1.4.3",
"name": "@transia/ripple-binary-codec",
"version": "1.4.5",
"description": "XRP Ledger binary codec",
"files": [
"dist/*",
@@ -28,16 +28,20 @@
},
"repository": {
"type": "git",
"url": "git@github.com:XRPLF/xrpl.js.git"
"url": "git@github.com:Transia-RnD/xrpl.js.git"
},
"bugs": {
"url": "https://github.com/XRPLF/xrpl.js/issues"
"url": "https://github.com/Transia-RnD/xrpl.js/issues"
},
"homepage": "https://github.com/XRPLF/xrpl.js/tree/main/packages/ripple-binary-codec#readme",
"homepage": "https://github.com/Transia-RnD/xrpl.js/tree/main/packages/ripple-binary-codec#readme",
"license": "ISC",
"readmeFilename": "README.md",
"prettier": "@xrplf/prettier-config",
"engines": {
"node": ">= 10"
},
"gitHead": "6c25d08019d06534525a4a2bf41bf0e135a79849",
"publishConfig": {
"access": "public"
}
}

View File

@@ -44,11 +44,16 @@
"NegativeUNL": 78,
"NFTokenPage": 80,
"NFTokenOffer": 55,
"URIToken": 85,
"Any": -3,
"Child": -2,
"Nickname": 110,
"Contract": 99,
"GeneratorMap": 103
"GeneratorMap": 103,
"Hook": 72,
"HookState": 118,
"HookDefinition": 68,
"EmittedTxn": 69
},
"FIELDS": [
[
@@ -321,6 +326,16 @@
"type": "UInt16"
}
],
[
"NetworkID",
{
"nth": 1,
"isVLEncoded": false,
"isSerialized": true,
"isSigningField": true,
"type": "UInt32"
}
],
[
"Flags",
{
@@ -761,6 +776,16 @@
"type": "UInt32"
}
],
[
"LockCount",
{
"nth": 47,
"isVLEncoded": false,
"isSerialized": true,
"isSigningField": true,
"type": "UInt32"
}
],
[
"IndexNext",
{
@@ -891,16 +916,6 @@
"type": "UInt64"
}
],
[
"HookOn",
{
"nth": 16,
"isVLEncoded": false,
"isSerialized": true,
"isSigningField": true,
"type": "UInt64"
}
],
[
"HookInstructionCount",
{
@@ -1151,6 +1166,16 @@
"type": "Hash256"
}
],
[
"HookOn",
{
"nth": 20,
"isVLEncoded": false,
"isSerialized": true,
"isSigningField": true,
"type": "Hash256"
}
],
[
"Digest",
{
@@ -1281,6 +1306,36 @@
"type": "Hash256"
}
],
[
"OfferID",
{
"nth": 34,
"isVLEncoded": false,
"isSerialized": true,
"isSigningField": true,
"type": "Hash256"
}
],
[
"EscrowID",
{
"nth": 35,
"isVLEncoded": false,
"isSerialized": true,
"isSigningField": true,
"type": "Hash256"
}
],
[
"URITokenID",
{
"nth": 36,
"isVLEncoded": false,
"isSerialized": true,
"isSigningField": true,
"type": "Hash256"
}
],
[
"Amount",
{
@@ -1421,6 +1476,26 @@
"type": "Amount"
}
],
[
"HookCallbackFee",
{
"nth": 20,
"isVLEncoded": false,
"isSerialized": true,
"isSigningField": true,
"type": "Amount"
}
],
[
"LockedBalance",
{
"nth": 21,
"isVLEncoded": false,
"isSerialized": true,
"isSigningField": true,
"type": "Amount"
}
],
[
"PublicKey",
{
@@ -1661,6 +1736,16 @@
"type": "Blob"
}
],
[
"Blob",
{
"nth": 26,
"isVLEncoded": true,
"isSerialized": true,
"isSigningField": true,
"type": "Blob"
}
],
[
"Account",
{
@@ -1801,6 +1886,16 @@
"type": "Vector256"
}
],
[
"HookNamespaces",
{
"nth": 5,
"isVLEncoded": true,
"isSerialized": true,
"isSigningField": true,
"type": "Vector256"
}
],
[
"Paths",
{
@@ -2176,6 +2271,10 @@
"telCAN_NOT_QUEUE_BLOCKED": -389,
"telCAN_NOT_QUEUE_FEE": -388,
"telCAN_NOT_QUEUE_FULL": -387,
"telWRONG_NETWORK": -386,
"telREQUIRES_NETWORK_ID": -385,
"telNETWORK_ID_MAKES_TX_NON_CANONICAL": -384,
"telNON_LOCAL_EMITTED_TXN": -383,
"temMALFORMED": -299,
"temBAD_AMOUNT": -298,
@@ -2215,6 +2314,16 @@
"temUNKNOWN": -264,
"temSEQ_AND_TICKET": -263,
"temBAD_NFTOKEN_TRANSFER_FEE": -262,
"temAMM_BAD_TOKENS": -261,
"temEQUAL_DOOR_ACCOUNTS": -260,
"temBAD_XCHAIN_PROOF": -259,
"temSIDECHAIN_BAD_ISSUES": -258,
"temSIDECHAIN_NONDOOR_OWNER": -257,
"temXCHAIN_BRIDGE_BAD_MIN_ACCOUNT_CREATE_AMOUNT": -256,
"temXCHAIN_BRIDGE_BAD_REWARD_AMOUNT": -255,
"temXCHAIN_TOO_MANY_ATTESTATIONS": -254,
"temHOOK_DATA_TOO_LARGE": -253,
"temHOOK_REJECTED": -252,
"tefFAILURE": -199,
"tefALREADY": -198,
@@ -2250,6 +2359,7 @@
"terNO_RIPPLE": -90,
"terQUEUED": -89,
"terPRE_TICKET": -88,
"terNO_HOOK": -87,
"tesSUCCESS": 0,
@@ -2291,6 +2401,7 @@
"tecKILLED": 150,
"tecHAS_OBLIGATIONS": 151,
"tecTOO_SOON": 152,
"tecHOOK_REJECTED": 153,
"tecMAX_SEQUENCE_REACHED": 154,
"tecNO_SUITABLE_NFTOKEN_PAGE": 155,
"tecNFTOKEN_BUY_SELL_MISMATCH": 156,
@@ -2298,7 +2409,32 @@
"tecCANT_ACCEPT_OWN_NFTOKEN_OFFER": 158,
"tecINSUFFICIENT_FUNDS": 159,
"tecOBJECT_NOT_FOUND": 160,
"tecINSUFFICIENT_PAYMENT": 161
"tecINSUFFICIENT_PAYMENT": 161,
"tecAMM_UNFUNDED": 162,
"tecAMM_BALANCE": 163,
"tecAMM_FAILED_DEPOSIT": 164,
"tecAMM_FAILED_WITHDRAW": 165,
"tecAMM_INVALID_TOKENS": 166,
"tecAMM_FAILED_BID": 167,
"tecAMM_FAILED_VOTE": 168,
"tecREQUIRES_FLAG": 169,
"tecPRECISION_LOSS": 170,
"tecBAD_XCHAIN_TRANSFER_ISSUE": 171,
"tecXCHAIN_NO_CLAIM_ID": 172,
"tecXCHAIN_BAD_CLAIM_ID": 173,
"tecXCHAIN_CLAIM_NO_QUORUM": 174,
"tecXCHAIN_PROOF_UNKNOWN_KEY": 175,
"tecXCHAIN_CREATE_ACCOUNT_NONXRP_ISSUE": 176,
"tecXCHAIN_WRONG_CHAIN": 177,
"tecXCHAIN_REWARD_MISMATCH": 178,
"tecXCHAIN_NO_SIGNERS_LIST": 179,
"tecXCHAIN_SENDING_ACCOUNT_MISMATCH": 180,
"tecXCHAIN_INSUFF_CREATE_AMOUNT": 181,
"tecXCHAIN_ACCOUNT_CREATE_PAST": 182,
"tecXCHAIN_ACCOUNT_CREATE_TOO_MANY": 183,
"tecXCHAIN_PAYMENT_FAILED": 184,
"tecXCHAIN_SELF_COMMIT": 185,
"tecXCHAIN_BAD_PUBLIC_KEY_ACCOUNT_PAIR": 186
},
"TRANSACTION_TYPES": {
"Invalid": -1,
@@ -2330,8 +2466,15 @@
"NFTokenCreateOffer": 27,
"NFTokenCancelOffer": 28,
"NFTokenAcceptOffer": 29,
"URITokenMint": 45,
"URITokenBurn": 46,
"URITokenBuy": 47,
"URITokenCreateSellOffer": 48,
"URITokenCancelSellOffer": 49,
"Invoke": 99,
"EnableAmendment": 100,
"SetFee": 101,
"UNLModify": 102
"UNLModify": 102,
"EmitFailure": 103
}
}

View File

@@ -19,6 +19,7 @@ const Field = DEFAULT_DEFINITIONS.field
* @brief: All valid transaction types
*/
const TRANSACTION_TYPES = DEFAULT_DEFINITIONS.transactionNames
const TRANSACTION_TYPE_MAP = DEFAULT_DEFINITIONS.transactionMap
export {
Bytes,
@@ -31,4 +32,5 @@ export {
TransactionResult,
TransactionType,
TRANSACTION_TYPES,
TRANSACTION_TYPE_MAP,
}

View File

@@ -33,6 +33,8 @@ class XrplDefinitionsBase {
transactionType: BytesLookup
// Valid transaction names
transactionNames: string[]
// Valid transaction names
transactionMap: Record<string, number>
// Maps serializable types to their TypeScript class implementation
dataTypes: Record<string, typeof SerializedType>
@@ -68,10 +70,20 @@ class XrplDefinitionsBase {
enums.FIELDS as Array<[string, FieldInfo]>,
enums.TYPES,
)
this.transactionNames = Object.entries(enums.TRANSACTION_TYPES)
.filter(([_key, value]) => value >= 0)
.map(([key, _value]) => key)
const ignoreList = ['EnableAmendment', 'SetFee', 'UNLModify', 'EmitFailure']
this.transactionMap = Object.assign(
{},
...Object.entries(enums.TRANSACTION_TYPES)
.filter(([_key, _value]) => _value >= 0 || ignoreList.includes(_key))
.map(([key, value]) => ({ [key]: value })),
)
this.dataTypes = {} // Filled in via associateTypes
this.associateTypes(types)
}

View File

@@ -6,6 +6,7 @@ import { JsonObject } from './types/serialized-type'
import {
XrplDefinitionsBase,
TRANSACTION_TYPES,
TRANSACTION_TYPE_MAP,
DEFAULT_DEFINITIONS,
} from './enums'
import { XrplDefinitions } from './enums/xrpl-definitions'
@@ -134,6 +135,7 @@ export {
decodeQuality,
decodeLedgerData,
TRANSACTION_TYPES,
TRANSACTION_TYPE_MAP,
XrplDefinitions,
XrplDefinitionsBase,
DEFAULT_DEFINITIONS,

View File

@@ -1,6 +1,6 @@
{
"name": "ripple-keypairs",
"version": "1.1.5",
"name": "@transia/ripple-keypairs",
"version": "1.1.7",
"description": "Cryptographic key pairs for the XRP Ledger",
"scripts": {
"build": "tsc -b",
@@ -25,11 +25,15 @@
},
"repository": {
"type": "git",
"url": "git@github.com:XRPLF/xrpl.js.git"
"url": "git@github.com:Transia-RnD/xrpl.js.git"
},
"license": "ISC",
"prettier": "@xrplf/prettier-config",
"engines": {
"node": ">= 10"
},
"gitHead": "40d9c2b1a158ab480df7e8b1c7dc2ed3b88afc75",
"publishConfig": {
"access": "public"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "xrpl",
"version": "2.7.0",
"name": "@transia/xrpl",
"version": "2.7.2",
"license": "ISC",
"description": "A TypeScript/JavaScript API for interacting with the XRP Ledger in Node.js and the browser",
"files": [
@@ -75,10 +75,14 @@
"prettier": "@xrplf/prettier-config",
"repository": {
"type": "git",
"url": "git@github.com:XRPLF/xrpl.js.git"
"url": "git@github.com:Transia-RnD/xrpl.js.git"
},
"readmeFilename": "README.md",
"engines": {
"node": ">=10.13.0"
},
"gitHead": "40d9c2b1a158ab480df7e8b1c7dc2ed3b88afc75",
"publishConfig": {
"access": "public"
}
}

View File

@@ -51,6 +51,83 @@ interface PathStep {
export type Path = PathStep[]
/**
* The object that describes the grant in HookGrants.
*/
export interface HookGrant {
/**
* The object that describes the grant in HookGrants.
*/
HookGrant: {
/**
* The hook hash of the grant.
*/
HookHash: string
/**
* The account authorized on the grant.
*/
Authorize?: string
}
}
/**
* The object that describes the parameter in HookParameters.
*/
export interface HookParameter {
/**
* The object that describes the parameter in HookParameters.
*/
HookParameter: {
/**
* The name of the parameter.
*/
HookParameterName: string
/**
* The value of the parameter.
*/
HookParameterValue: string
}
}
/**
* The object that describes the hook in Hooks.
*/
export interface Hook {
/**
* The object that describes the hook in Hooks.
*/
Hook: {
/**
* The code that is executed when the hook is triggered.
*/
CreateCode: string
/**
* The flags that are set on the hook.
*/
Flags: number
/**
* The transactions that triggers the hook. Represented as a 256Hash
*/
HookOn?: string
/**
* The namespace of the hook.
*/
HookNamespace?: string
/**
* The API version of the hook.
*/
HookApiVersion?: number
/**
* The parameters of the hook.
*/
HookParameters?: HookParameter[]
/**
* The grants of the hook.
*/
HookGrants?: HookGrant[]
}
}
/**
* The object that describes the signer in SignerEntries.
*/

View File

@@ -0,0 +1,20 @@
import { Transaction } from '../transactions'
import BaseLedgerEntry from './BaseLedgerEntry'
/**
* The EmittedTxn object type contains the
*
* @category Ledger Entries
*/
export default interface EmittedTxn extends BaseLedgerEntry {
LedgerEntryType: 'EmittedTxn'
EmittedTxn: Transaction
/**
* A hint indicating which page of the sender's owner directory links to this
* object, in case the directory consists of multiple pages.
*/
OwnerNode: string
}

View File

@@ -0,0 +1,27 @@
import { Hook as WHook } from '../common'
import BaseLedgerEntry from './BaseLedgerEntry'
/**
* The Hook object type contains the
*
* @category Ledger Entries
*/
export default interface Hook extends BaseLedgerEntry {
LedgerEntryType: 'Hook'
/** The identifying (classic) address of this account. */
Account: string
/**
* A hint indicating which page of the sender's owner directory links to this
* object, in case the directory consists of multiple pages.
*/
OwnerNode: string
PreviousTxnID: string
PreviousTxnLgrSeq: number
Hooks: WHook[]
}

View File

@@ -0,0 +1,67 @@
import { HookParameter } from '../common'
import BaseLedgerEntry from './BaseLedgerEntry'
/**
* The HookDefintion object type contains the
*
* @category Ledger Entries
*/
export default interface HookDefintion extends BaseLedgerEntry {
LedgerEntryType: 'HookDefintion'
/**
* The flags that are set on the hook.
*/
Flags: number
/**
* This field contains a string that is used to uniquely identify the hook.
*/
HookHash: string
/**
* The transactions that triggers the hook. Represented as a 256Hash
*/
HookOn?: string
/**
* The namespace of the hook.
*/
HookNamespace?: string
/**
* The API version of the hook.
*/
HookApiVersion?: string
/**
* The parameters of the hook.
*/
HookParameters?: HookParameter[]
/**
* The code that is executed when the hook is triggered.
*/
CreateCode?: string
/**
* This is an optional field that contains the transaction ID of the hook set.
*/
HookSetTxnID?: string
/**
* This is an optional field that contains the number of references to this hook.
*/
ReferenceCount?: number
/**
* This is an optional field that contains the fee associated with the hook.
*/
Fee?: string
/**
* This is an optional field that contains the callback fee associated with the hook.
*/
HookCallbackFee?: number
}

View File

@@ -0,0 +1,29 @@
import BaseLedgerEntry from './BaseLedgerEntry'
/**
* The HookState object type contains the
*
* @category Ledger Entries
*/
export default interface HookState extends BaseLedgerEntry {
LedgerEntryType: 'HookState'
/**
* A hint indicating which page of the sender's owner directory links to this
* object, in case the directory consists of multiple pages.
*/
OwnerNode: string
/**
* The HookStateKey property contains the key associated with this hook state,
* and the HookStateData property contains the data associated with this hook state.
*/
HookStateKey: string
/**
* The `HookStateData` property contains the data associated with this hook state.
* It is typically a string containing the data associated with this hook state,
* such as an identifier or other information.
*/
HookStateData: string
}

View File

@@ -3,8 +3,12 @@ import Amendments from './Amendments'
import Check from './Check'
import DepositPreauth from './DepositPreauth'
import DirectoryNode from './DirectoryNode'
import EmittedTxn from './EmittedTxn'
import Escrow from './Escrow'
import FeeSettings from './FeeSettings'
import Hook from './Hook'
import HookDefinition from './HookDefinition'
import HookState from './HookState'
import LedgerHashes from './LedgerHashes'
import NegativeUNL from './NegativeUNL'
import Offer from './Offer'
@@ -19,8 +23,12 @@ type LedgerEntry =
| Check
| DepositPreauth
| DirectoryNode
| EmittedTxn
| Escrow
| FeeSettings
| Hook
| HookDefinition
| HookState
| LedgerHashes
| NegativeUNL
| Offer

View File

@@ -6,8 +6,12 @@ import Amendments from './Amendments'
import Check from './Check'
import DepositPreauth from './DepositPreauth'
import DirectoryNode from './DirectoryNode'
import EmittedTxn from './EmittedTxn'
import Escrow from './Escrow'
import FeeSettings from './FeeSettings'
import Hook from './Hook'
import HookDefinition from './HookDefinition'
import HookState from './HookState'
import Ledger from './Ledger'
import LedgerEntry from './LedgerEntry'
import LedgerHashes from './LedgerHashes'
@@ -26,8 +30,12 @@ export {
Check,
DepositPreauth,
DirectoryNode,
EmittedTxn,
Escrow,
FeeSettings,
Hook,
HookDefinition,
HookState,
Ledger,
LedgerEntry,
LedgerHashes,

View File

@@ -137,6 +137,40 @@ export interface LedgerEntryRequest extends BaseRequest {
ticket_sequence: number
}
| string
/**
* The object ID of a transaction emitted by the ledger entry.
*/
emitted_txn?: string
/**
* The hash of the Hook object to retrieve.
*/
hook_definition?: string
/**
* The Hook object to retrieve. If a string, must be the object ID of the Hook.
* If an object, requires `account` sub-field.
*/
hook?:
| {
/** The account of the Hook object. */
account: string
}
| string
/**
* Object specifying the HookState object to retrieve. Requires the sub-fields
* `account`, `key`, and `namespace_id` to uniquely specify the HookState entry
* to retrieve.
*/
hook_state?: {
/** The account of the Hook object. */
account: string
/** The key of the state. */
key: string
/** The namespace of the state. */
namespace_id: string
}
}
/**

View File

@@ -159,6 +159,8 @@ export interface BaseTransaction {
* account it says it is from.
*/
TxnSignature?: string
NetworkID?: number
}
/**

View File

@@ -14,6 +14,7 @@ export { DepositPreauth } from './depositPreauth'
export { EscrowCancel } from './escrowCancel'
export { EscrowCreate } from './escrowCreate'
export { EscrowFinish } from './escrowFinish'
export { Invoke } from './invoke'
export { NFTokenAcceptOffer } from './NFTokenAcceptOffer'
export { NFTokenBurn } from './NFTokenBurn'
export { NFTokenCancelOffer } from './NFTokenCancelOffer'
@@ -42,6 +43,7 @@ export {
export { PaymentChannelCreate } from './paymentChannelCreate'
export { PaymentChannelFund } from './paymentChannelFund'
export { SetRegularKey } from './setRegularKey'
export { SetHook } from './setHook'
export { SignerListSet } from './signerListSet'
export { TicketCreate } from './ticketCreate'
export { TrustSetFlagsInterface, TrustSetFlags, TrustSet } from './trustSet'

View File

@@ -0,0 +1,32 @@
import { ValidationError } from '../../errors'
import { BaseTransaction, validateBaseTransaction } from './common'
/**
*
*
* @category Transaction Models
*/
export interface Invoke extends BaseTransaction {
TransactionType: 'Invoke'
/**
* If present, invokes the Hook on the Destination account.
*/
Destination?: string
}
/**
* Verify the form and type of an Invoke at runtime.
*
* @param tx - An Invoke Transaction.
* @throws When the Invoke is Malformed.
*/
export function validateInvoke(tx: Record<string, unknown>): void {
validateBaseTransaction(tx)
if (tx.Account === tx.Destination) {
throw new ValidationError(
'Invoke: Destination and Account must not be equal',
)
}
}

View File

@@ -0,0 +1,56 @@
import { ValidationError } from '../../errors'
import { Hook } from '../common'
import { BaseTransaction, validateBaseTransaction } from './common'
/**
*
*
* @category Transaction Models
*/
export interface SetHook extends BaseTransaction {
TransactionType: 'SetHook'
/**
*
*/
Hooks: Hook[]
}
const MAX_HOOKS = 4
const HEX_REGEX = /^[0-9A-Fa-f]{64}$/u
/**
* Verify the form and type of an SetHook at runtime.
*
* @param tx - An SetHook Transaction.
* @throws When the SetHook is Malformed.
*/
export function validateSetHook(tx: Record<string, unknown>): void {
validateBaseTransaction(tx)
if (!Array.isArray(tx.Hooks)) {
throw new ValidationError('SetHook: invalid Hooks')
}
if (tx.Hooks.length > MAX_HOOKS) {
throw new ValidationError(
`SetHook: maximum of ${MAX_HOOKS} hooks allowed in Hooks`,
)
}
for (const hook of tx.Hooks) {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Should be a Hook
const hookObject = hook as Hook
const { HookOn, HookNamespace } = hookObject.Hook
if (HookOn !== undefined && !HEX_REGEX.test(HookOn)) {
throw new ValidationError(
`SetHook: HookOn in Hook must be a 256-bit (32-byte) hexadecimal value`,
)
}
if (HookNamespace !== undefined && !HEX_REGEX.test(HookNamespace)) {
throw new ValidationError(
`SetHook: HookNamespace in Hook must be a 256-bit (32-byte) hexadecimal value`,
)
}
}
}

View File

@@ -43,6 +43,7 @@ import {
PaymentChannelFund,
validatePaymentChannelFund,
} from './paymentChannelFund'
import { SetHook, validateSetHook } from './setHook'
import { SetRegularKey, validateSetRegularKey } from './setRegularKey'
import { SignerListSet, validateSignerListSet } from './signerListSet'
import { TicketCreate, validateTicketCreate } from './ticketCreate'
@@ -72,6 +73,7 @@ export type Transaction =
| PaymentChannelClaim
| PaymentChannelCreate
| PaymentChannelFund
| SetHook
| SetRegularKey
| SignerListSet
| TicketCreate
@@ -188,6 +190,10 @@ export function validate(transaction: Record<string, unknown>): void {
validateSetRegularKey(tx)
break
case 'SetHook':
validateSetHook(tx)
break
case 'SignerListSet':
validateSignerListSet(tx)
break

View File

@@ -8,7 +8,7 @@ import { Transaction } from '../models/transactions'
import { setTransactionFlagsToNumber } from '../models/utils/flags'
import { xrpToDrops } from '../utils'
import getFeeXrp from './getFeeXrp'
import { getFeeXrp } from './getFeeXrp'
// Expire unconfirmed transactions after 20 ledger versions, approximately 1 minute, by default
const LEDGER_OFFSET = 20

View File

@@ -14,7 +14,7 @@ const BASE_10 = 10
* @param cushion - The fee cushion to use.
* @returns The transaction fee.
*/
export default async function getFeeXrp(
export async function getFeeXrp(
client: Client,
cushion?: number,
): Promise<string> {
@@ -43,3 +43,22 @@ export default async function getFeeXrp(
// Round fee to 6 decimal places
return new BigNumber(fee.toFixed(NUM_DECIMAL_PLACES)).toString(BASE_10)
}
/**
* Calculates the estimated transaction fee.
* Note: This is a public API that can be called directly.
*
* @param client - The Client used to connect to the ledger.
* @param txBlob - The encoded transaction to estimate the fee for.
* @returns The transaction fee.
*/
export async function getFeeEstimateXrp(
client: Client,
txBlob: string,
): Promise<string> {
const response = await client.request({
command: 'fee',
tx_blob: txBlob,
})
return response.result.drops.base_fee
}

View File

@@ -5,6 +5,7 @@ export { getBalances, getXrpBalance } from './balances'
export { default as getLedgerIndex } from './getLedgerIndex'
export { default as getOrderbook } from './getOrderbook'
export { getFeeXrp, getFeeEstimateXrp } from './getFeeXrp'
export * from './submit'

View File

@@ -0,0 +1,114 @@
/**
* @module tts
* @description
* This module contains the transaction types and the function to calculate the hook on
*/
// eslint-disable-next-line @typescript-eslint/no-require-imports -- Required
import createHash = require('create-hash')
import { TRANSACTION_TYPES, TRANSACTION_TYPE_MAP } from 'ripple-binary-codec'
import { XrplError } from '../errors'
import { HookParameter } from '../models/common'
/**
* @constant tts
* @description
* Transaction types
*/
/**
* @typedef TTS
* @description
* Transaction types
*/
export type TTS = typeof TRANSACTION_TYPE_MAP
/**
* Calculate the hook on
*
* @param arr - array of transaction types
* @returns the hook on
*/
export function calculateHookOn(arr: Array<keyof TTS>): string {
let hash =
'0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffff'
arr.forEach((nth) => {
if (typeof nth !== 'string') {
throw new XrplError(`HookOn transaction type must be string`)
}
if (!TRANSACTION_TYPES.includes(String(nth))) {
throw new XrplError(
`invalid transaction type '${String(nth)}' in HookOn array`,
)
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- Required
const tts: Record<string, number> = TRANSACTION_TYPE_MAP
let value = BigInt(hash)
// eslint-disable-next-line no-bitwise -- Required
value ^= BigInt(1) << BigInt(tts[nth])
// eslint-disable-next-line @typescript-eslint/no-magic-numbers -- Required
hash = `0x${value.toString(16)}`
})
hash = hash.replace('0x', '')
// eslint-disable-next-line @typescript-eslint/no-magic-numbers -- Required
hash = hash.padStart(64, '0')
return hash.toUpperCase()
}
/**
* Calculate the sha256 of a string
*
* @param string - the string to calculate the sha256
* @returns the sha256
*/
export async function sha256(string: string): Promise<string> {
const hash = createHash('sha256')
hash.update(string)
const hashBuffer = hash.digest()
const hashArray = Array.from(new Uint8Array(hashBuffer))
const hashHex = hashArray
// eslint-disable-next-line @typescript-eslint/no-magic-numbers -- Required
.map((bytes) => bytes.toString(16).padStart(2, '0'))
.join('')
return hashHex
}
/**
* Calculate the hex of a namespace
*
* @param namespace - the namespace to calculate the hex
* @returns the hex
*/
export async function hexNamespace(namespace: string): Promise<string> {
return (await sha256(namespace)).toUpperCase()
}
/**
* Calculate the hex of the hook parameters
*
* @param data - the hook parameters
* @returns the hex of the hook parameters
*/
export function hexHookParameters(data: HookParameter[]): HookParameter[] {
const hookParameters: HookParameter[] = []
for (const parameter of data) {
hookParameters.push({
HookParameter: {
HookParameterName: Buffer.from(
parameter.HookParameter.HookParameterName,
'utf8',
)
.toString('hex')
.toUpperCase(),
HookParameterValue: Buffer.from(
parameter.HookParameter.HookParameterValue,
'utf8',
)
.toString('hex')
.toUpperCase(),
},
})
}
return hookParameters
}

View File

@@ -40,6 +40,7 @@ import {
hashEscrow,
hashPaymentChannel,
} from './hashes'
import { calculateHookOn, hexNamespace, hexHookParameters, TTS } from './hooks'
import parseNFTokenID from './parseNFTokenID'
import {
percentToTransferRate,
@@ -222,4 +223,8 @@ export {
getNFTokenID,
createCrossChainPayment,
parseNFTokenID,
calculateHookOn,
hexNamespace,
hexHookParameters,
TTS,
}

View File

@@ -1,6 +1,6 @@
import { assert } from 'chai'
import getFeeXrp from '../../src/sugar/getFeeXrp'
import { getFeeXrp } from '../../src/sugar/getFeeXrp'
import rippled from '../fixtures/rippled'
import {
setupClient,

View File

@@ -0,0 +1,150 @@
import { assert } from 'chai'
import { validate, ValidationError } from '../../src'
import { validateSetHook } from '../../src/models/transactions/setHook'
/**
* SetHook Transaction Verification Testing.
*
* Providing runtime verification testing for each specific transaction type.
*/
describe('SetHook', function () {
let setHookTx
beforeEach(function () {
setHookTx = {
Flags: 0,
TransactionType: 'SetHook',
Account: 'rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn',
Fee: '12',
Hooks: [
{
Hook: {
CreateCode:
'0061736D01000000011C0460057F7F7F7F7F017E60037F7F7E017E60027F7F017F60017F017E02230303656E76057472616365000003656E7606616363657074000103656E76025F670002030201030503010002062B077F0141B088040B7F004180080B7F0041A6080B7F004180080B7F0041B088040B7F0041000B7F0041010B07080104686F6F6B00030AC4800001C0800001017F230041106B220124002001200036020C41920841134180084112410010001A410022002000420010011A41012200200010021A200141106A240042000B0B2C01004180080B254163636570742E633A2043616C6C65642E00224163636570742E633A2043616C6C65642E22',
HookOn:
'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFFFF7',
Flags: 1,
HookApiVersion: 0,
HookNamespace:
'4FF9961269BF7630D32E15276569C94470174A5DA79FA567C0F62251AA9A36B9',
},
},
],
} as any
})
it(`verifies valid SetHook`, function () {
assert.doesNotThrow(() => validateSetHook(setHookTx))
assert.doesNotThrow(() => validate(setHookTx))
})
// it(`throws w/ empty Hooks`, function () {
// setHookTx.Hooks = []
// assert.throws(
// () => validateSetHook(setHookTx),
// ValidationError,
// 'SetHook: need at least 1 member in Hooks',
// )
// assert.throws(
// () => validate(setHookTx),
// ValidationError,
// 'SetHook: need at least 1 member in Hooks',
// )
// })
it(`throws w/ invalid Hooks`, function () {
setHookTx.Hooks = 'khgfgyhujk'
assert.throws(
() => validateSetHook(setHookTx),
ValidationError,
'SetHook: invalid Hooks',
)
assert.throws(
() => validate(setHookTx),
ValidationError,
'SetHook: invalid Hooks',
)
})
it(`throws w/ maximum of 4 members allowed in Hooks`, function () {
setHookTx.Hooks = []
const hook = {
Hook: {
CreateCode:
'0061736D01000000011C0460057F7F7F7F7F017E60037F7F7E017E60027F7F017F60017F017E02230303656E76057472616365000003656E7606616363657074000103656E76025F670002030201030503010002062B077F0141B088040B7F004180080B7F0041A6080B7F004180080B7F0041B088040B7F0041000B7F0041010B07080104686F6F6B00030AC4800001C0800001017F230041106B220124002001200036020C41920841134180084112410010001A410022002000420010011A41012200200010021A200141106A240042000B0B2C01004180080B254163636570742E633A2043616C6C65642E00224163636570742E633A2043616C6C65642E22',
HookOn:
'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFFFF7',
Flags: 1,
HookApiVersion: 0,
HookNamespace:
'4FF9961269BF7630D32E15276569C94470174A5DA79FA567C0F62251AA9A36B9',
},
}
setHookTx.Hooks.push(hook)
setHookTx.Hooks.push(hook)
setHookTx.Hooks.push(hook)
setHookTx.Hooks.push(hook)
setHookTx.Hooks.push(hook)
const errorMessage = 'SetHook: maximum of 4 hooks allowed in Hooks'
assert.throws(
() => validateSetHook(setHookTx),
ValidationError,
errorMessage,
)
assert.throws(() => validate(setHookTx), ValidationError, errorMessage)
})
it(`throws w/ invalid HookOn in Hooks`, function () {
setHookTx.SignerQuorum = 2
setHookTx.Hooks = [
{
Hook: {
CreateCode:
'0061736D01000000011C0460057F7F7F7F7F017E60037F7F7E017E60027F7F017F60017F017E02230303656E76057472616365000003656E7606616363657074000103656E76025F670002030201030503010002062B077F0141B088040B7F004180080B7F0041A6080B7F004180080B7F0041B088040B7F0041000B7F0041010B07080104686F6F6B00030AC4800001C0800001017F230041106B220124002001200036020C41920841134180084112410010001A410022002000420010011A41012200200010021A200141106A240042000B0B2C01004180080B254163636570742E633A2043616C6C65642E00224163636570742E633A2043616C6C65642E22',
HookOn: '',
Flags: 1,
HookApiVersion: 0,
HookNamespace:
'4FF9961269BF7630D32E15276569C94470174A5DA79FA567C0F62251AA9A36B9',
},
},
]
const errorMessage =
'SetHook: HookOn in Hook must be a 256-bit (32-byte) hexadecimal value'
assert.throws(
() => validateSetHook(setHookTx),
ValidationError,
errorMessage,
)
assert.throws(() => validate(setHookTx), ValidationError, errorMessage)
})
it(`throws w/ invalid HookNamespace in Hooks`, function () {
setHookTx.SignerQuorum = 2
setHookTx.Hooks = [
{
Hook: {
CreateCode:
'0061736D01000000011C0460057F7F7F7F7F017E60037F7F7E017E60027F7F017F60017F017E02230303656E76057472616365000003656E7606616363657074000103656E76025F670002030201030503010002062B077F0141B088040B7F004180080B7F0041A6080B7F004180080B7F0041B088040B7F0041000B7F0041010B07080104686F6F6B00030AC4800001C0800001017F230041106B220124002001200036020C41920841134180084112410010001A410022002000420010011A41012200200010021A200141106A240042000B0B2C01004180080B254163636570742E633A2043616C6C65642E00224163636570742E633A2043616C6C65642E22',
HookOn:
'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFFFF7',
Flags: 1,
HookApiVersion: 0,
HookNamespace: '',
},
},
]
const errorMessage =
'SetHook: HookNamespace in Hook must be a 256-bit (32-byte) hexadecimal value'
assert.throws(
() => validateSetHook(setHookTx),
ValidationError,
errorMessage,
)
assert.throws(() => validate(setHookTx), ValidationError, errorMessage)
})
})

View File

@@ -0,0 +1,64 @@
import { assert } from 'chai'
import {
calculateHookOn,
hexNamespace,
hexHookParameters,
TTS,
} from '../../src'
describe('test hook on', function () {
it('invalid', function () {
const invokeOn: Array<keyof TTS> = ['AccountSet1']
expect(() => {
calculateHookOn(invokeOn)
}).toThrow("invalid transaction type 'AccountSet1' in HookOn array")
})
it('all', function () {
const result = calculateHookOn([])
assert.equal(
result,
'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFFFFF',
)
})
it('one', function () {
const invokeOn: Array<keyof TTS> = ['AccountSet']
const result = calculateHookOn(invokeOn)
assert.equal(
result,
'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFFFF7',
)
})
})
describe('test hook namespace', function () {
it('basic', async function () {
const result = await hexNamespace('starter')
assert.equal(
result,
'4FF9961269BF7630D32E15276569C94470174A5DA79FA567C0F62251AA9A36B9',
)
})
})
describe('test hook parameters', function () {
it('basic', async function () {
const parameters = [
{
HookParameter: {
HookParameterName: 'name1',
HookParameterValue: 'value1',
},
},
]
const result = hexHookParameters(parameters)
assert.deepEqual(result, [
{
HookParameter: {
HookParameterName: '6E616D6531',
HookParameterValue: '76616C756531',
},
},
])
})
})