Skip to content

Commit e0a5698

Browse files
authored
release v14.6.0 (#1341)
* update stellar-base dependency and bump version to v14.6.0 * add e2e test for non-simulated SAC transfer transaction
1 parent d66da20 commit e0a5698

5 files changed

Lines changed: 296 additions & 7 deletions

File tree

.github/workflows/format.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,11 @@ jobs:
2323
run: yarn install --network-concurrency 1
2424

2525
- name: Run Linter Checks
26-
run: yarn fmt && (git diff-index --quiet HEAD; git diff)
26+
run: |
27+
yarn fmt
28+
if ! git diff-index --quiet HEAD; then
29+
echo "::error::Formatting issues detected. Please run 'yarn fmt' locally and commit the changes."
30+
git diff --stat
31+
git diff
32+
exit 1
33+
fi

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ A breaking change will get clearly marked in this log.
66

77
## Unreleased
88

9+
## [v14.6.0](https://github.com/stellar/js-stellar-sdk/compare/v14.5.0...v14.6.0)
10+
11+
### Added
12+
* Upgraded underlying `@stellar/stellar-base` library to include its new features and fixes ([release notes](https://github.com/stellar/js-stellar-base/releases/tag/v14.1.0)).
13+
914
## [v14.5.0](https://github.com/stellar/js-stellar-sdk/compare/v14.4.3...v14.5.0)
1015

1116
### Added

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@stellar/stellar-sdk",
3-
"version": "14.5.0",
3+
"version": "14.6.0",
44
"description": "A library for working with the Stellar network, including communication with the Horizon and Soroban RPC servers.",
55
"keywords": [
66
"stellar"
@@ -198,7 +198,7 @@
198198
"webpack-cli": "^5.0.1"
199199
},
200200
"dependencies": {
201-
"@stellar/stellar-base": "^14.0.4",
201+
"@stellar/stellar-base": "^14.1.0",
202202
"axios": "^1.13.3",
203203
"bignumber.js": "^9.3.1",
204204
"commander": "^14.0.2",
Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
import { expect, describe, it, beforeAll } from "vitest";
2+
import {
3+
Asset,
4+
BASE_FEE,
5+
Keypair,
6+
Operation,
7+
TransactionBuilder,
8+
rpc,
9+
} from "../../../lib";
10+
import { generateFundedKeypair, networkPassphrase, server } from "./util";
11+
12+
const TRANSFER_AMOUNT = 1_0000000n; // 1 token with 7 decimals
13+
14+
/**
15+
* Helper to build, sign, send, and poll a transaction with a single signer.
16+
*/
17+
async function submitTx(
18+
sourceKeypair: Keypair,
19+
op: ReturnType<
20+
| typeof Operation.changeTrust
21+
| typeof Operation.payment
22+
| typeof Operation.createStellarAssetContract
23+
>,
24+
) {
25+
const account = await server.getAccount(sourceKeypair.publicKey());
26+
const builder = new TransactionBuilder(account, {
27+
fee: BASE_FEE,
28+
networkPassphrase,
29+
}).setTimeout(30);
30+
31+
builder.addOperation(op);
32+
33+
const tx = builder.build();
34+
if (
35+
op.body().switch().name === "changeTrust" ||
36+
op.body().switch().name === "payment"
37+
) {
38+
// Classic operations can not be simulated, so we send them directly without simulation to ensure the test setup is complete.
39+
tx.sign(sourceKeypair);
40+
const classicResp = await server.sendTransaction(tx);
41+
const classicResult = await server.pollTransaction(classicResp.hash, {
42+
attempts: 15,
43+
});
44+
if (classicResult.status !== rpc.Api.GetTransactionStatus.SUCCESS) {
45+
throw new Error(`Classic transaction failed: ${classicResult.status}`);
46+
}
47+
return classicResult;
48+
}
49+
const sim = await server.simulateTransaction(tx);
50+
if (rpc.Api.isSimulationError(sim)) {
51+
throw new Error(`Simulation failed: ${sim.error}`);
52+
}
53+
const assembled = rpc.assembleTransaction(tx, sim).build();
54+
assembled.sign(sourceKeypair);
55+
56+
const resp = await server.sendTransaction(assembled);
57+
const result = await server.pollTransaction(resp.hash, { attempts: 15 });
58+
if (result.status !== rpc.Api.GetTransactionStatus.SUCCESS) {
59+
throw new Error(`Transaction failed: ${result.status}`);
60+
}
61+
return result;
62+
}
63+
64+
let context: {
65+
sender: Keypair;
66+
receiver: Keypair;
67+
issuer: Keypair;
68+
customAsset: Asset;
69+
};
70+
71+
// These tests are to ensure that the SAC transfer operation works end-to-end on a real network,
72+
// and to catch any issues with the way the operation is built.
73+
// They are not meant to be exhaustive tests of all possible transfer scenarios,
74+
// but rather a sanity check that basic transfers succeed without errors.
75+
describe("Non-simulated SAC Transfer", () => {
76+
beforeAll(async () => {
77+
const sender = await generateFundedKeypair();
78+
const receiver = await generateFundedKeypair();
79+
const issuer = await generateFundedKeypair();
80+
81+
const customAsset = new Asset("TST", issuer.publicKey());
82+
83+
// 1. Establish trustlines for the custom asset
84+
await submitTx(sender, Operation.changeTrust({ asset: customAsset }));
85+
await submitTx(receiver, Operation.changeTrust({ asset: customAsset }));
86+
87+
// 2. Issuer mints tokens to sender and receiver
88+
await submitTx(
89+
issuer,
90+
Operation.payment({
91+
destination: sender.publicKey(),
92+
asset: customAsset,
93+
amount: "100",
94+
}),
95+
);
96+
await submitTx(
97+
issuer,
98+
Operation.payment({
99+
destination: receiver.publicKey(),
100+
asset: customAsset,
101+
amount: "100",
102+
}),
103+
);
104+
105+
// 3. Deploy the Stellar Asset Contract for the custom asset and native
106+
await submitTx(
107+
issuer,
108+
Operation.createStellarAssetContract({ asset: customAsset }),
109+
);
110+
await submitTx(
111+
issuer,
112+
Operation.createStellarAssetContract({ asset: Asset.native() }),
113+
);
114+
115+
context = { sender, receiver, issuer, customAsset };
116+
});
117+
118+
describe("native asset transfers", () => {
119+
it("should transfer native XLM between user accounts", async () => {
120+
const { sender, receiver } = context;
121+
122+
const account = await server.getAccount(sender.publicKey());
123+
const tx = new TransactionBuilder(account, {
124+
fee: BASE_FEE,
125+
networkPassphrase,
126+
})
127+
.setTimeout(30)
128+
.addSacTransferOperation(
129+
receiver.publicKey(),
130+
Asset.native(),
131+
TRANSFER_AMOUNT,
132+
)
133+
.build();
134+
135+
tx.sign(sender);
136+
137+
const response = await server.sendTransaction(tx);
138+
expect(response.errorResult).toBeUndefined();
139+
140+
const result = await server.pollTransaction(response.hash, {
141+
attempts: 10,
142+
});
143+
expect(result.status).toBe(rpc.Api.GetTransactionStatus.SUCCESS);
144+
});
145+
146+
it("should transfer native XLM from user to contract address", async () => {
147+
const { sender, customAsset } = context;
148+
const contractId = customAsset.contractId(networkPassphrase);
149+
150+
const account = await server.getAccount(sender.publicKey());
151+
const tx = new TransactionBuilder(account, {
152+
fee: BASE_FEE,
153+
networkPassphrase,
154+
})
155+
.setTimeout(30)
156+
.addSacTransferOperation(contractId, Asset.native(), TRANSFER_AMOUNT)
157+
.build();
158+
159+
tx.sign(sender);
160+
161+
const response = await server.sendTransaction(tx);
162+
expect(response.errorResult).toBeUndefined();
163+
164+
const result = await server.pollTransaction(response.hash, {
165+
attempts: 10,
166+
});
167+
expect(result.status).toBe(rpc.Api.GetTransactionStatus.SUCCESS);
168+
});
169+
});
170+
171+
describe("custom SAC token transfers", () => {
172+
it("should transfer a custom SAC token between user accounts", async () => {
173+
const { sender, receiver, customAsset } = context;
174+
175+
const account = await server.getAccount(sender.publicKey());
176+
const tx = new TransactionBuilder(account, {
177+
fee: BASE_FEE,
178+
networkPassphrase,
179+
})
180+
.setTimeout(30)
181+
.addSacTransferOperation(
182+
receiver.publicKey(),
183+
customAsset,
184+
TRANSFER_AMOUNT,
185+
)
186+
.build();
187+
188+
tx.sign(sender);
189+
190+
const response = await server.sendTransaction(tx);
191+
expect(response.errorResult).toBeUndefined();
192+
193+
const result = await server.pollTransaction(response.hash, {
194+
attempts: 10,
195+
});
196+
expect(result.status).toBe(rpc.Api.GetTransactionStatus.SUCCESS);
197+
});
198+
199+
it("should transfer a custom SAC token from user to contract address", async () => {
200+
const { sender, customAsset } = context;
201+
const contractId = customAsset.contractId(networkPassphrase);
202+
203+
const account = await server.getAccount(sender.publicKey());
204+
const tx = new TransactionBuilder(account, {
205+
fee: BASE_FEE,
206+
networkPassphrase,
207+
})
208+
.setTimeout(30)
209+
.addSacTransferOperation(contractId, customAsset, TRANSFER_AMOUNT)
210+
.build();
211+
212+
tx.sign(sender);
213+
214+
const response = await server.sendTransaction(tx);
215+
expect(response.errorResult).toBeUndefined();
216+
217+
const result = await server.pollTransaction(response.hash, {
218+
attempts: 10,
219+
});
220+
expect(result.status).toBe(rpc.Api.GetTransactionStatus.SUCCESS);
221+
});
222+
223+
it("should transfer a custom SAC token when sender is the issuer", async () => {
224+
const { issuer, receiver, customAsset } = context;
225+
226+
const account = await server.getAccount(issuer.publicKey());
227+
const tx = new TransactionBuilder(account, {
228+
fee: BASE_FEE,
229+
networkPassphrase,
230+
})
231+
.setTimeout(30)
232+
.addSacTransferOperation(
233+
receiver.publicKey(),
234+
customAsset,
235+
TRANSFER_AMOUNT,
236+
)
237+
.build();
238+
239+
tx.sign(issuer);
240+
241+
const response = await server.sendTransaction(tx);
242+
expect(response.errorResult).toBeUndefined();
243+
244+
const result = await server.pollTransaction(response.hash, {
245+
attempts: 10,
246+
});
247+
expect(result.status).toBe(rpc.Api.GetTransactionStatus.SUCCESS);
248+
});
249+
250+
it("should transfer a custom SAC token when receiver is the issuer", async () => {
251+
const { sender, issuer, customAsset } = context;
252+
253+
const account = await server.getAccount(sender.publicKey());
254+
const tx = new TransactionBuilder(account, {
255+
fee: BASE_FEE,
256+
networkPassphrase,
257+
})
258+
.setTimeout(30)
259+
.addSacTransferOperation(
260+
issuer.publicKey(),
261+
customAsset,
262+
TRANSFER_AMOUNT,
263+
)
264+
.build();
265+
266+
tx.sign(sender);
267+
268+
const response = await server.sendTransaction(tx);
269+
expect(response.errorResult).toBeUndefined();
270+
271+
const result = await server.pollTransaction(response.hash, {
272+
attempts: 10,
273+
});
274+
expect(result.status).toBe(rpc.Api.GetTransactionStatus.SUCCESS);
275+
});
276+
});
277+
});

yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1756,10 +1756,10 @@
17561756
resolved "https://registry.yarnpkg.com/@stellar/js-xdr/-/js-xdr-3.1.2.tgz#db7611135cf21e989602fd72f513c3bed621bc74"
17571757
integrity sha512-VVolPL5goVEIsvuGqDc5uiKxV03lzfWdvYg1KikvwheDmTBO68CKDji3bAZ/kppZrx5iTA8z3Ld5yuytcvhvOQ==
17581758

1759-
"@stellar/stellar-base@^14.0.4":
1760-
version "14.0.4"
1761-
resolved "https://registry.yarnpkg.com/@stellar/stellar-base/-/stellar-base-14.0.4.tgz#530511679588e8440277ded071e3a46a387c9fff"
1762-
integrity sha512-UbNW6zbdOBXJwLAV2mMak0bIC9nw3IZVlQXkv2w2dk1jgCbJjy3oRVC943zeGE5JAm0Z9PHxrIjmkpGhayY7kw==
1759+
"@stellar/stellar-base@^14.1.0":
1760+
version "14.1.0"
1761+
resolved "https://registry.yarnpkg.com/@stellar/stellar-base/-/stellar-base-14.1.0.tgz#5093aaabd03eb1364833fb88c26a9e20f857e7d8"
1762+
integrity sha512-A8kFli6QGy22SRF45IjgPAJfUNGjnI+R7g4DF5NZYVsD1kGf7B4ITyc4OPclLV9tqNI4/lXxafGEw0JEUbHixw==
17631763
dependencies:
17641764
"@noble/curves" "^1.9.6"
17651765
"@stellar/js-xdr" "^3.1.2"

0 commit comments

Comments
 (0)