Skip to content

Commit d6ba366

Browse files
committed
1 parent bed9356 commit d6ba366

File tree

15 files changed

+874
-3
lines changed

15 files changed

+874
-3
lines changed

.github/workflows/nodejs.yml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,3 +269,48 @@ jobs:
269269
id: docs-artifact
270270
if: steps.check-tag.outputs.published_version_tag == 'true'
271271
uses: actions/deploy-pages@v4
272+
273+
snippets:
274+
if: |
275+
(github.event_name == 'push' && github.ref == 'refs/heads/main') ||
276+
github.event_name == 'workflow_dispatch'
277+
runs-on: ubuntu-latest
278+
timeout-minutes: 10
279+
280+
strategy:
281+
max-parallel: 1
282+
matrix:
283+
node-version: [20.x, 22.x]
284+
285+
steps:
286+
- uses: actions/checkout@v4
287+
- name: Use Node.js ${{ matrix.node-version }}
288+
uses: actions/setup-node@v4
289+
with:
290+
node-version: ${{ matrix.node-version }}
291+
292+
- name: Setup npm version 10
293+
run: |
294+
npm i -g npm@10 --registry=https://registry.npmjs.org
295+
296+
- name: Cache node modules
297+
id: cache-nodemodules
298+
uses: actions/cache@v4
299+
env:
300+
cache-name: cache-node-modules
301+
with:
302+
# caching node_modules
303+
path: |
304+
node_modules
305+
*/*/node_modules
306+
key: ${{ runner.os }}-deps-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
307+
restore-keys: |
308+
${{ runner.os }}-deps-${{ matrix.node-version }}-
309+
310+
- name: Install Dependencies
311+
if: steps.cache-nodemodules.outputs.cache-hit != 'true'
312+
run: npm ci
313+
314+
- run: npm run build
315+
- name: Run Snippets
316+
run: (for i in packages/xrpl/snippets/src/*.ts; do echo "Running $i" && npx ts-node $i || exit 1; done)

packages/ripple-keypairs/src/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,11 @@ function derivePublicKey(privateKey: string): string {
101101
if (algorithm === 'ecdsa-secp256k1') {
102102
return secp256k1.deriveKeypairFromPrivateKey(privateKey).publicKey
103103
}
104-
return ed25519.deriveKeypairFromPrivateKey(privateKey).publicKey
104+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
105+
if (algorithm === 'ed25519') {
106+
return ed25519.deriveKeypairFromPrivateKey(privateKey).publicKey
107+
}
108+
throw new Error('Unknown signing scheme algorithm')
105109
}
106110

107111
function deriveNodeAddress(publicKey: string): string {

packages/xrpl/.eslintrc.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,17 @@ module.exports = {
7676
'@typescript-eslint/no-magic-numbers': 'off',
7777
},
7878
},
79+
{
80+
files: ['snippets/src/*.ts'],
81+
rules: {
82+
'import/no-unused-modules': 'off',
83+
// Each file has a particular flow.
84+
'max-lines-per-function': 'off',
85+
'max-statements': 'off',
86+
// Snippets have logs on console to better understand the working.
87+
'no-console': 'off',
88+
},
89+
},
7990
{
8091
files: ['test/**/*.ts'],
8192
rules: {

packages/xrpl/package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@
4747
"ws": "^8.14.2"
4848
},
4949
"scripts": {
50-
"build": "run-s build:lib build:web",
50+
"build": "run-s build:lib build:snippets build:web",
51+
"build:snippets": "tsc --build ./snippets/tsconfig.json",
5152
"build:lib": "tsc --build tsconfig.build.json",
5253
"build:web": "webpack",
5354
"build:browserTests": "webpack --config ./test/webpack.config.js",
@@ -64,7 +65,10 @@
6465
"test:watch": "jest --watch --config=jest.config.unit.js --verbose false --silent=false",
6566
"format": "prettier --write '{src,test}/**/*.ts'",
6667
"lint": "eslint . --ext .ts --max-warnings 0",
67-
"perf": "./scripts/perf_test.sh"
68+
"perf": "./scripts/perf_test.sh",
69+
"compile:snippets": "tsc -p snippets/tsconfig.json",
70+
"start:snippet": "npm run compile:snippets && node",
71+
"inspect:snippet": "npm run compile:snippets && node inspect"
6872
},
6973
"prettier": "@xrplf/prettier-config",
7074
"repository": {
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import {
2+
AccountObjectsRequest,
3+
Client,
4+
PaymentChannelCreate,
5+
PaymentChannelClaim,
6+
hashes,
7+
} from '../../src'
8+
9+
const client = new Client('wss://s.altnet.rippletest.net:51233')
10+
11+
void claimPayChannel()
12+
13+
// The snippet walks us through creating and claiming a Payment Channel.
14+
async function claimPayChannel(): Promise<void> {
15+
await client.connect()
16+
17+
// creating wallets as prerequisite
18+
const { wallet: wallet1 } = await client.fundWallet()
19+
const { wallet: wallet2 } = await client.fundWallet()
20+
21+
console.log('Balances of wallets before Payment Channel is claimed:')
22+
console.log(await client.getXrpBalance(wallet1.classicAddress))
23+
console.log(await client.getXrpBalance(wallet2.classicAddress))
24+
25+
// create a Payment Channel and submit and wait for tx to be validated
26+
const paymentChannelCreate: PaymentChannelCreate = {
27+
TransactionType: 'PaymentChannelCreate',
28+
Account: wallet1.classicAddress,
29+
Amount: '100',
30+
Destination: wallet2.classicAddress,
31+
SettleDelay: 86400,
32+
PublicKey: wallet1.publicKey,
33+
}
34+
35+
const paymentChannelResponse = await client.submitAndWait(
36+
paymentChannelCreate,
37+
{ wallet: wallet1 },
38+
)
39+
console.log(paymentChannelResponse)
40+
41+
// check that the object was actually created
42+
const accountObjectsRequest: AccountObjectsRequest = {
43+
command: 'account_objects',
44+
account: wallet1.classicAddress,
45+
}
46+
47+
const accountObjects = (await client.request(accountObjectsRequest)).result
48+
.account_objects
49+
50+
console.log(accountObjects)
51+
52+
// destination claims the Payment Channel and we see the balances to verify.
53+
const paymentChannelClaim: PaymentChannelClaim = {
54+
Account: wallet2.classicAddress,
55+
TransactionType: 'PaymentChannelClaim',
56+
Channel: hashes.hashPaymentChannel(
57+
wallet1.classicAddress,
58+
wallet2.classicAddress,
59+
paymentChannelResponse.result.tx_json.Sequence ?? 0,
60+
),
61+
Amount: '100',
62+
}
63+
64+
const channelClaimResponse = await client.submit(paymentChannelClaim, {
65+
wallet: wallet1,
66+
})
67+
console.log(channelClaimResponse)
68+
69+
console.log('Balances of wallets after Payment Channel is claimed:')
70+
console.log(await client.getXrpBalance(wallet1.classicAddress))
71+
console.log(await client.getXrpBalance(wallet2.classicAddress))
72+
73+
await client.disconnect()
74+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { Client } from '../../src'
2+
3+
const client = new Client('wss://s2.ripple.com:51233')
4+
5+
async function getTransaction(): Promise<void> {
6+
await client.connect()
7+
const ledger = await client.request({
8+
command: 'ledger',
9+
transactions: true,
10+
ledger_index: 'validated',
11+
})
12+
console.log(ledger)
13+
14+
const transactions = ledger.result.ledger.transactions
15+
if (transactions && transactions.length > 0) {
16+
const tx = await client.request({
17+
command: 'tx',
18+
transaction: transactions[0],
19+
})
20+
console.log(tx)
21+
22+
// The meta field can be undefined if the transaction has not been validated yet (and so has not changed the ledger).
23+
if (tx.result.meta == null) {
24+
throw new Error('meta not included in the response')
25+
}
26+
27+
/*
28+
* delivered_amount is the amount actually received by the destination account.
29+
* Use this field to determine how much was delivered, regardless of whether the transaction is a partial payment.
30+
* https://xrpl.org/transaction-metadata.html#delivered_amount
31+
*/
32+
if (typeof tx.result.meta !== 'string') {
33+
console.log('delivered_amount:', tx.result.meta.delivered_amount)
34+
}
35+
}
36+
37+
await client.disconnect()
38+
}
39+
40+
void getTransaction()
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import {
2+
multisign,
3+
Client,
4+
AccountSet,
5+
convertStringToHex,
6+
SignerListSet,
7+
Wallet,
8+
} from '../../src'
9+
10+
const client = new Client('wss://s.altnet.rippletest.net:51233')
11+
12+
async function multisigning(): Promise<void> {
13+
await client.connect()
14+
/*
15+
* This wallet creation is for demonstration purposes.
16+
* In practice, users generally will not have all keys in one spot,
17+
* hence, users need to implement a way to get signatures.
18+
*/
19+
const wallet1 = Wallet.generate()
20+
const wallet2 = Wallet.generate()
21+
const { wallet: walletMaster } = await client.fundWallet(null, {
22+
usageContext: 'code snippets',
23+
})
24+
const signerListSet: SignerListSet = {
25+
TransactionType: 'SignerListSet',
26+
Account: walletMaster.classicAddress,
27+
SignerEntries: [
28+
{
29+
SignerEntry: {
30+
Account: wallet1.classicAddress,
31+
SignerWeight: 1,
32+
},
33+
},
34+
{
35+
SignerEntry: {
36+
Account: wallet2.classicAddress,
37+
SignerWeight: 1,
38+
},
39+
},
40+
],
41+
SignerQuorum: 2,
42+
}
43+
44+
const signerListResponse = await client.submit(signerListSet, {
45+
wallet: walletMaster,
46+
})
47+
console.log('SignerListSet constructed successfully:')
48+
console.log(signerListResponse)
49+
50+
const accountSet: AccountSet = {
51+
TransactionType: 'AccountSet',
52+
Account: walletMaster.classicAddress,
53+
Domain: convertStringToHex('example.com'),
54+
}
55+
const accountSetTx = await client.autofill(accountSet, 2)
56+
console.log('AccountSet transaction is ready to be multisigned:')
57+
console.log(accountSetTx)
58+
const { tx_blob: tx_blob1 } = wallet1.sign(accountSetTx, true)
59+
const { tx_blob: tx_blob2 } = wallet2.sign(accountSetTx, true)
60+
const multisignedTx = multisign([tx_blob1, tx_blob2])
61+
const submitResponse = await client.submit(multisignedTx)
62+
63+
if (submitResponse.result.engine_result === 'tesSUCCESS') {
64+
console.log('The multisigned transaction was accepted by the ledger:')
65+
console.log(submitResponse)
66+
if (submitResponse.result.tx_json.Signers) {
67+
console.log(
68+
`The transaction had ${submitResponse.result.tx_json.Signers.length} signatures`,
69+
)
70+
}
71+
} else {
72+
console.log(
73+
"The multisigned transaction was rejected by rippled. Here's the response from rippled:",
74+
)
75+
console.log(submitResponse)
76+
}
77+
78+
await client.disconnect()
79+
}
80+
81+
void multisigning()
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { Client, Payment, PaymentFlags, TrustSet } from '../../src'
2+
3+
const client = new Client('wss://s.altnet.rippletest.net:51233')
4+
5+
// This snippet walks us through partial payment.
6+
async function partialPayment(): Promise<void> {
7+
await client.connect()
8+
9+
// creating wallets as prerequisite
10+
const { wallet: wallet1 } = await client.fundWallet(null, {
11+
usageContext: 'code snippets',
12+
})
13+
const { wallet: wallet2 } = await client.fundWallet(null, {
14+
usageContext: 'code snippets',
15+
})
16+
17+
// create a trustline to issue an IOU `FOO` and set limit on it.
18+
const trust_set_tx: TrustSet = {
19+
TransactionType: 'TrustSet',
20+
Account: wallet2.classicAddress,
21+
LimitAmount: {
22+
currency: 'FOO',
23+
issuer: wallet1.classicAddress,
24+
// Value for the new IOU - 10000000000 - is arbitarily chosen.
25+
value: '10000000000',
26+
},
27+
}
28+
29+
await client.submitAndWait(trust_set_tx, {
30+
wallet: wallet2,
31+
})
32+
33+
console.log('Balances after trustline is created')
34+
console.log(await client.getBalances(wallet1.classicAddress))
35+
console.log(await client.getBalances(wallet2.classicAddress))
36+
37+
// Initially, the issuer(wallet1) sends an amount to the other account(wallet2)
38+
const issue_quantity = '3840'
39+
const payment: Payment = {
40+
TransactionType: 'Payment',
41+
Account: wallet1.classicAddress,
42+
Amount: {
43+
currency: 'FOO',
44+
value: issue_quantity,
45+
issuer: wallet1.classicAddress,
46+
},
47+
Destination: wallet2.classicAddress,
48+
}
49+
50+
// submit payment
51+
const initialPayment = await client.submitAndWait(payment, {
52+
wallet: wallet1,
53+
})
54+
console.log(initialPayment)
55+
56+
console.log('Balances after issuer(wallet1) sends IOU("FOO") to wallet2')
57+
console.log(await client.getBalances(wallet1.classicAddress))
58+
console.log(await client.getBalances(wallet2.classicAddress))
59+
60+
/*
61+
* Send money less than the amount specified on 2 conditions:
62+
* 1. Sender has less money than the aamount specified in the payment Tx.
63+
* 2. Sender has the tfPartialPayment flag activated.
64+
*
65+
* Other ways to specify flags are by using Hex code and decimal code.
66+
* eg. For partial payment(tfPartialPayment)
67+
* decimal ->131072, hex -> 0x00020000
68+
*/
69+
const partialPaymentTx: Payment = {
70+
TransactionType: 'Payment',
71+
Account: wallet2.classicAddress,
72+
Amount: {
73+
currency: 'FOO',
74+
value: '4000',
75+
issuer: wallet1.classicAddress,
76+
},
77+
Destination: wallet1.classicAddress,
78+
Flags: PaymentFlags.tfPartialPayment,
79+
}
80+
81+
// submit payment
82+
const submitResponse = await client.submitAndWait(partialPaymentTx, {
83+
wallet: wallet2,
84+
})
85+
console.log(submitResponse)
86+
87+
console.log(
88+
"Balances after Partial Payment, when wallet2 tried to send 4000 FOO's",
89+
)
90+
console.log(await client.getBalances(wallet1.classicAddress))
91+
console.log(await client.getBalances(wallet2.classicAddress))
92+
93+
await client.disconnect()
94+
}
95+
void partialPayment()

0 commit comments

Comments
 (0)