Skip to content

Commit

Permalink
Release/1.2.0 v3 (#64)
Browse files Browse the repository at this point in the history
* add sponsoring (#45)

* finish

* return types and exporting

* only return from parent

* use lambda function way

* finish (#46)

* make basefee optional (#49)

* Add tests for Muxed accounts (#48)

* add yarn prepare command for husky (#50)

* remove gitignore

* test commit with long line

* fix

* test husky

* test messed up formatting

* run prettier on all files

---------

Co-authored-by: Cássio Marcos Goulart <cassiomgoulart@gmail.com>

* differentiate between browser and node builds (#47)

* differentiate between browser and node builds

* bump pkg version

* treat node as the default

* Update README (#51)

* Update README

* Add anchor initialization

* Release/1.1.2 (#54)

* only report transactions with changed status (#52)

* only emit txn if status changed

* cleanup

* fix watcher stopping

* cleanup

* bump package version

---------

Co-authored-by: Alec Charbonneau <aleccharb21@gmail.com>

* add default domain signer and default client_domain (#53)

* add default domainsigner

* allow custom client

* remove gitignore

* add default client domain

* cleanup

* use mandatary headers object

* add js doc comment

* adding path payment and swap (#56)

* first cut

* swap

* clean up

* add jsdoc

* use both strict send and receive

* not not

* add sep24 example code (#58)

* Release/1.1.3 (#61)

* check if withdraw memo is hash type (#57)

* check if withdraw memo is hash type

* parse hash better

* upgrade stellar-sdk version (#59)

* bump package version

---------

Co-authored-by: Alec Charbonneau <aleccharb21@gmail.com>

* SEP-24 example improvement (#62)

* bump package version

---------

Co-authored-by: Alec Charbonneau <aleccharb21@gmail.com>
Co-authored-by: Cássio Marcos Goulart <3228151+CassioMG@users.noreply.github.com>
Co-authored-by: Cássio Marcos Goulart <cassiomgoulart@gmail.com>
Co-authored-by: Piyal Basu <pbasu235@gmail.com>
  • Loading branch information
5 people authored Sep 22, 2023
1 parent 936bbb9 commit a97ea11
Show file tree
Hide file tree
Showing 19 changed files with 1,215 additions and 43 deletions.
22 changes: 22 additions & 0 deletions examples/sep24/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Sep-24 examples for stellar typescript-wallet-sdk

## Example code

To view how the wallet-sdk can be used to create sep-24 deposits and withdrawals
look at `sep24.ts`.

## Running deposit and withdrawals

To see them in action you can run below from the project root:

```
$ yarn example:sep24
```

This will run the deposit flow and watch for it to finish. At the end it will
ask if you'd like to run the withdraw flow. Use USDC as the asset for the
example.

Progress will be logged in the terminal.

_note: the identity values used in the sep24 interactive portal can all be fake_
199 changes: 199 additions & 0 deletions examples/sep24/sep24.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
import axios from "axios";
import readline from "readline";

import {
walletSdk,
Anchor,
SigningKeypair,
Types,
IssuedAssetId,
} from "../../src";
import { Memo, MemoText } from "stellar-sdk";

const wallet = walletSdk.Wallet.TestNet();
const stellar = wallet.stellar();
const anchor = wallet.anchor({ homeDomain: "testanchor.stellar.org" });
const account = stellar.account();

let kp: SigningKeypair;

const asset = new IssuedAssetId(
"USDC",
"GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5",
);

const runSep24 = async () => {
await createAccount();
await runDeposit(anchor, kp);
await runDepositWatcher(anchor);

while (!depositDone) {
await new Promise((resolve) => setTimeout(resolve, 1000));
}

const ans = await askQuestion("Do you want to start withdrawal? (y/n)");
if (ans !== "y") {
process.exit(0);
}

await runWithdraw(anchor, kp);
await runWithdrawWatcher(anchor, kp);
};

// Create Account
const createAccount = async () => {
console.log("creating account ...");
kp = account.createKeypair();
console.log(`kp: \n${kp.publicKey}\n${kp.secretKey}`);

// funding new account
await axios.get("https://friendbot.stellar.org/?addr=" + kp.publicKey);

const txBuilder = await stellar.transaction({
sourceAddress: kp,
baseFee: 1000,
});
const tx = txBuilder.addAssetSupport(asset).build();
kp.sign(tx);
await stellar.submitTransaction(tx);
};

// Create Deposit
let authToken: string;
export const runDeposit = async (anchor: Anchor, kp: SigningKeypair) => {
console.log("\ncreating deposit ...");
const auth = await anchor.sep10();
authToken = await auth.authenticate({ accountKp: kp });

const resp = await anchor.sep24().deposit({
assetCode: asset.code,
authToken: authToken,
lang: "en-US",
destinationMemo: new Memo(MemoText, "test-memo"),
// Optional field. Same result would be achieved with omitting this field.
// Replace with a different account if you want to change destination
destinationAccount: kp.publicKey,
// If not specified, amount will be collected in the interactive flow. You can also pass extra SEP-9 fields.
extraFields: { amount: "10" },
});

console.log("Open url:\n", resp.url);
};

// Watch Deposit
export let depositDone = false;
export const runDepositWatcher = (anchor: Anchor) => {
console.log("\nstarting watcher ...");

let stop: Types.WatcherStopFunction;
const onMessage = (m: Types.AnchorTransaction) => {
console.log({ m });
if (m.status === Types.TransactionStatus.completed) {
console.log("status completed, stopping watcher");
stop();
depositDone = true;
}
};

const onError = (error: Types.AnchorTransaction | Error) => {
console.error({ error });
};

const watcher = anchor.sep24().watcher();
const resp = watcher.watchAllTransactions({
authToken: authToken,
assetCode: asset.code,
onMessage,
onError,
timeout: 5000,
lang: "en-US",
});

stop = resp.stop;
};

// Create Withdrawal
export const runWithdraw = async (anchor, kp) => {
console.log("\ncreating withdrawal ...");

const resp = await anchor.sep24().withdraw({
assetCode: asset.code,
authToken: authToken,
lang: "en-US",
// Optional field. Same result would be achieved with omitting this field.
// Replace with a different account if you want to change destination
withdrawalAccount: kp.publicKey,
// If not specified, amount will be collected in the interactive flow. You can also pass extra SEP-9 fields.
extraFields: { amount: "10" },
});
console.log("Open url:\n", resp.url);
};

const sendWithdrawalTransaction = async (withdrawalTxn, kp) => {
const asset = new IssuedAssetId(
"USDC",
"GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5",
);

const txBuilder = await stellar.transaction({
sourceAddress: kp,
baseFee: 1000,
});
const tx = txBuilder
.transferWithdrawalTransaction(withdrawalTxn, asset)
.build();
kp.sign(tx);
await stellar.submitTransaction(tx);
};

// Watch Withdrawal
export const runWithdrawWatcher = (anchor, kp) => {
console.log("\nstarting watcher ...");

let stop;
const onMessage = (m) => {
console.log({ m });

if (m.status === Types.TransactionStatus.pending_user_transfer_start) {
sendWithdrawalTransaction(m, kp);
}

if (m.status === Types.TransactionStatus.completed) {
console.log("status completed, stopping watcher");

stop();
}
};

const onError = (e) => {
console.error({ e });
};

const watcher = anchor.sep24().watcher();
const resp = watcher.watchAllTransactions({
authToken: authToken,
assetCode: asset.code,
onMessage,
onError,
timeout: 5000,
lang: "en-US",
});

stop = resp.stop;
};

export const askQuestion = (query) => {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});

return new Promise((resolve) =>
rl.question(query, (ans) => {
rl.close();
resolve(ans);
}),
);
};

runSep24();
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@stellar/typescript-wallet-sdk",
"version": "1.1.3",
"version": "1.2.0",
"engines": {
"node": ">=18"
},
Expand Down Expand Up @@ -29,6 +29,7 @@
"stream-browserify": "^3.0.0",
"ts-jest": "^29.0.5",
"ts-loader": "^9.4.2",
"ts-node": "^10.9.1",
"tslib": "^2.5.0",
"typescript": "^5.0.4",
"webpack": "^5.83.1",
Expand All @@ -47,10 +48,11 @@
"utility-types": "^3.10.0"
},
"scripts": {
"prepare": "yarn build",
"prepare": "husky install",
"test": "jest --watchAll",
"build:web": "webpack --config webpack.config.js",
"build:node": "webpack --env NODE=true --config webpack.config.js",
"build": "run-p build:web build:node"
"build": "run-p build:web build:node",
"example:sep24": "ts-node examples/sep24/sep24.ts"
}
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export {
AccountService,
Stellar,
TransactionBuilder,
SponsoringBuilder,
} from "./walletSdk/Horizon";
export { Recovery } from "./walletSdk/Recovery";
export { Watcher } from "./walletSdk/Watcher";
Expand Down
2 changes: 2 additions & 0 deletions src/walletSdk/Anchor/Sep24.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ export class Sep24 {

async deposit({
assetCode,

authToken,

lang,
extraFields,
destinationMemo,
Expand Down
65 changes: 64 additions & 1 deletion src/walletSdk/Auth/WalletSigner.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import { Transaction } from "stellar-sdk";
import {
Transaction,
TransactionBuilder as StellarTransactionBuilder,
FeeBumpTransaction,
} from "stellar-sdk";
import { AxiosInstance } from "axios";

import {
SignWithClientAccountParams,
SignWithDomainAccountParams,
HttpHeaders,
} from "../Types";
import { AccountKeypair } from "../Horizon/Account";
import { DefaultClient } from "../";

export interface WalletSigner {
signWithClientAccount({
Expand All @@ -28,3 +37,57 @@ export const DefaultSigner: WalletSigner = {
);
},
};

/**
* Represents a Domain Signer used for signing Stellar transactions with a domain server.
*
* @class
* @implements {WalletSigner}
*/
export class DomainSigner implements WalletSigner {
private url: string;
private client: AxiosInstance;
private headers: HttpHeaders;

/**
* Create a new instance of the DomainSigner class.
*
* @constructor
* @param {string} url - The URL of the domain server.
* @param {HttpHeaders} headers - The HTTP headers for requests to the domain server.
* These headers can be used for authentication purposes.
*/
constructor(url: string, headers: HttpHeaders) {
this.url = url;
this.client = DefaultClient;
this.headers = headers;
}

signWithClientAccount({
transaction,
accountKp,
}: SignWithClientAccountParams): Transaction {
transaction.sign(accountKp.keypair);
return transaction;
}

async signWithDomainAccount({
transactionXDR,
networkPassphrase,
accountKp,
}: SignWithDomainAccountParams): Promise<Transaction> {
const response = await this.client.post(
this.url,
{
transactionXDR,
networkPassphrase,
},
{ headers: this.headers },
);

return StellarTransactionBuilder.fromXDR(
response.data.transaction,
networkPassphrase,
) as Transaction;
}
}
2 changes: 1 addition & 1 deletion src/walletSdk/Auth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export class Sep10 {
const challengeResponse = await this.challenge({
accountKp,
memoId,
clientDomain,
clientDomain: clientDomain || this.cfg.app.defaultClientDomain,
});
const signedTransaction = await this.sign({
accountKp,
Expand Down
6 changes: 6 additions & 0 deletions src/walletSdk/Exceptions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,12 @@ export class WithdrawalTxMissingMemoError extends Error {
}
}

export class PathPayOnlyOneAmountError extends Error {
constructor() {
super("Must give sendAmount or destAmount value, but not both");
Object.setPrototypeOf(this, PathPayOnlyOneAmountError.prototype);
}
}
export class WithdrawalTxMemoError extends Error {
constructor() {
super(`Error parsing withdrawal transaction memo`);
Expand Down
Loading

0 comments on commit a97ea11

Please sign in to comment.