@silentswap/sdk 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,6 @@
1
+ # SilentSwap SDK
2
+
3
+ ```bash
4
+ npm i @silentswap/sdk
5
+ ```
6
+
@@ -0,0 +1,29 @@
1
+ import type { Caip19, IntStr } from './types/core.js';
2
+ import type { Hex } from 'viem';
3
+ export declare enum Caip19Namespace {
4
+ BIP122 = "bip122",
5
+ EIP155 = "eip155",
6
+ COSMOS = "cosmos",
7
+ SOLANA = "solana",
8
+ SUI = "sui",
9
+ STELLAR = "stellar"
10
+ }
11
+ export interface Caip19Parts<N extends Caip19Namespace> {
12
+ chainNamespace: N;
13
+ chainId: N extends Caip19Namespace.EIP155 ? number | bigint | IntStr : N extends Caip19Namespace.SUI ? 'mainnet' | 'testnet' | 'devnet' : string;
14
+ assetNamespace: N extends Caip19Namespace.EIP155 ? 'erc20' : N extends Caip19Namespace.SOLANA ? 'token' : N extends Caip19Namespace.COSMOS ? 'ics20' : N extends Caip19Namespace.SUI ? 'slip44' : string;
15
+ assetReference: N extends Caip19Namespace.EIP155 ? Hex : N extends Caip19Namespace.SUI ? string : bigint | IntStr;
16
+ }
17
+ /**
18
+ * Helper function makes a CAIP-19 string from parts
19
+ * @param parts CAIP-19 parts
20
+ * @returns CAIP-19 string
21
+ */
22
+ export declare function caip19FromParts<N extends Caip19Namespace>(parts: Caip19Parts<N>): Caip19;
23
+ /**
24
+ * Helper function makes a CAIP-19 string for an EVM fungible token
25
+ * @param chainId EVM chain ID
26
+ * @param tokenAddress EVM token address
27
+ * @returns CAIP-19 string
28
+ */
29
+ export declare function caip19FungibleEvmToken(chainId: number, tokenAddress: Hex): Caip19;
package/dist/caip19.js ADDED
@@ -0,0 +1,31 @@
1
+ export var Caip19Namespace;
2
+ (function (Caip19Namespace) {
3
+ Caip19Namespace["BIP122"] = "bip122";
4
+ Caip19Namespace["EIP155"] = "eip155";
5
+ Caip19Namespace["COSMOS"] = "cosmos";
6
+ Caip19Namespace["SOLANA"] = "solana";
7
+ Caip19Namespace["SUI"] = "sui";
8
+ Caip19Namespace["STELLAR"] = "stellar";
9
+ })(Caip19Namespace || (Caip19Namespace = {}));
10
+ /**
11
+ * Helper function makes a CAIP-19 string from parts
12
+ * @param parts CAIP-19 parts
13
+ * @returns CAIP-19 string
14
+ */
15
+ export function caip19FromParts(parts) {
16
+ return `${parts.chainNamespace}:${parts.chainId}/${parts.assetNamespace}:${parts.assetReference}`;
17
+ }
18
+ /**
19
+ * Helper function makes a CAIP-19 string for an EVM fungible token
20
+ * @param chainId EVM chain ID
21
+ * @param tokenAddress EVM token address
22
+ * @returns CAIP-19 string
23
+ */
24
+ export function caip19FungibleEvmToken(chainId, tokenAddress) {
25
+ return caip19FromParts({
26
+ chainNamespace: Caip19Namespace.EIP155,
27
+ chainId,
28
+ assetNamespace: 'erc20',
29
+ assetReference: tokenAddress,
30
+ });
31
+ }
@@ -0,0 +1,69 @@
1
+ import { ENVIRONMENT } from './constants.js';
2
+ import type { AuthRequest, AuthResponse, NonceResponse, OrderRequest, OrderResponse, QuoteRequest, QuoteResponse } from './types/api.js';
3
+ import type { ResponseWrapper } from './types/sdk.js';
4
+ import type { Hex } from 'viem';
5
+ export type SilentSwapClientConfig = {
6
+ /**
7
+ * API key for authentication
8
+ */
9
+ apiKey: string;
10
+ /**
11
+ * Chain environment
12
+ */
13
+ environment: ENVIRONMENT;
14
+ /**
15
+ * Base URL to override the default
16
+ */
17
+ baseUrl?: string;
18
+ };
19
+ /**
20
+ * SilentSwap client
21
+ */
22
+ declare class SilentSwapClient {
23
+ #private;
24
+ protected config: SilentSwapClientConfig;
25
+ constructor(config: SilentSwapClientConfig);
26
+ /**
27
+ * Returns the gateway address for the current environment
28
+ */
29
+ get gatewayAddress(): Hex;
30
+ /**
31
+ * Returns the proxy public key for the current environment
32
+ */
33
+ get proxyPublicKey(): Hex;
34
+ /**
35
+ * Fetches a signed nonce from the backend for the given signer address
36
+ * @param addr - signer address
37
+ * @param init - request initialization options
38
+ * @returns a {@link ResponseWrapper `ResponseWrapper`}{@link NonceResponse `<NonceResponse>`}
39
+ */
40
+ nonce(addr: Hex, init?: RequestInit): Promise<ResponseWrapper<NonceResponse>>;
41
+ /**
42
+ * Authenticates a signer with the backend
43
+ * @param auth - authentication request
44
+ * @param init - request initialization options
45
+ * @returns a {@link ResponseWrapper `ResponseWrapper`}{@link AuthResponse `<AuthResponse>`}
46
+ */
47
+ authenticate(auth: AuthRequest, init?: RequestInit): Promise<ResponseWrapper<AuthResponse>>;
48
+ /**
49
+ * Fetches a signed quote from the backend
50
+ * @param quote - quote request
51
+ * @param init - request initialization options
52
+ * @returns a {@link ResponseWrapper `ResponseWrapper`}{@link QuoteResponse `<QuoteResponse>`}
53
+ */
54
+ quote(quote: QuoteRequest, init?: RequestInit): Promise<ResponseWrapper<QuoteResponse>>;
55
+ /**
56
+ * Creates a SilentSwap order, a prerequisite to depositing funds into the Gateway contract
57
+ * @param order - order request
58
+ * @param init - request initialization options
59
+ * @returns a {@link ResponseWrapper `ResponseWrapper`}{@link OrderResponse `<OrderResponse>`}
60
+ */
61
+ order(order: OrderRequest, init?: RequestInit): Promise<ResponseWrapper<OrderResponse>>;
62
+ }
63
+ export type { SilentSwapClient };
64
+ /**
65
+ * Creates a new SilentSwap client
66
+ * @param gc_client - client configuration
67
+ * @returns a new {@link SilentSwapClient}
68
+ */
69
+ export declare function createSilentSwapClient(gc_client: SilentSwapClientConfig): SilentSwapClient;
package/dist/client.js ADDED
@@ -0,0 +1,168 @@
1
+ import { ENVIRONMENT, MAINNET_GATEWAY_ADDRESS, MAINNET_GATEWAY_PUBLIC_KEY, MAINNET_SILENTSWAP_API } from './constants.js';
2
+ const ENVIRONMENT_CONFIGS = {
3
+ [ENVIRONMENT.MAINNET]: {
4
+ gatewayAddress: MAINNET_GATEWAY_ADDRESS,
5
+ proxyPublicKey: MAINNET_GATEWAY_PUBLIC_KEY,
6
+ },
7
+ };
8
+ /**
9
+ * SilentSwap client
10
+ */
11
+ class SilentSwapClient {
12
+ config;
13
+ constructor(config) {
14
+ this.config = config;
15
+ // unsupported environment
16
+ if (!['MAINNET'].includes(this.config.environment)) {
17
+ throw new Error(`Unsupported environment: ${this.config.environment}`);
18
+ }
19
+ }
20
+ /**
21
+ * Performs a GET request to the backend API
22
+ * @param path - path to request
23
+ * @param params - query parameters
24
+ * @param init - request initialization options
25
+ * @returns a {@link ResponseWrapper `<ResponseWrapper>`}`<Data>`
26
+ */
27
+ async #get(path, params = {}, init = {}) {
28
+ const { config } = this;
29
+ // submit GET request
30
+ const res = await fetch(`${(config.baseUrl ?? MAINNET_SILENTSWAP_API) + path}?${new URLSearchParams(params).toString()}`, {
31
+ ...init || {},
32
+ method: 'GET',
33
+ headers: {
34
+ ...init?.headers
35
+ ? Object === init.headers.constructor
36
+ ? init.headers
37
+ : () => { throw new Error('.headers value must be a plain object'); }
38
+ : {},
39
+ authorization: config.apiKey,
40
+ accept: 'application/json',
41
+ },
42
+ });
43
+ // read body
44
+ const resBody = await res.text();
45
+ // attempt to parse response body JSON
46
+ try {
47
+ const resJson = JSON.parse(resBody);
48
+ // OK respose
49
+ if (res.ok) {
50
+ return [void 0, resJson];
51
+ }
52
+ // not OK
53
+ else {
54
+ return [resJson, void 0];
55
+ }
56
+ }
57
+ catch (_err) {
58
+ return [{
59
+ type: 'SERVER_ERROR',
60
+ error: `Failed to parse server response:\n${resBody}`,
61
+ }, void 0];
62
+ }
63
+ }
64
+ /**
65
+ * Performs a POST request to the backend API
66
+ * @param path - path to request
67
+ * @param reqBody - request body JSON object
68
+ * @param init - request initialization options
69
+ * @returns a {@link ResponseWrapper `ResponseWrapper`}`<Data>`
70
+ */
71
+ async #post(path, reqBody, init = {}) {
72
+ const { config } = this;
73
+ // submit POST request
74
+ const res = await fetch((config.baseUrl ?? MAINNET_SILENTSWAP_API) + path, {
75
+ ...init || {},
76
+ method: 'POST',
77
+ headers: {
78
+ ...init?.headers
79
+ ? Object === init.headers.constructor
80
+ ? init.headers
81
+ : () => { throw new Error('.headers value must be a plain object'); }
82
+ : {},
83
+ 'authorization': config.apiKey,
84
+ 'accept': 'application/json',
85
+ 'content-type': 'application/json',
86
+ },
87
+ body: JSON.stringify(reqBody),
88
+ });
89
+ // read body
90
+ const resBody = await res.text();
91
+ // attempt to parse response body JSON
92
+ try {
93
+ const resJson = JSON.parse(resBody);
94
+ // OK respose
95
+ if (res.ok) {
96
+ return [void 0, resJson];
97
+ }
98
+ // not OK
99
+ else {
100
+ return [resJson, void 0];
101
+ }
102
+ }
103
+ catch (_err) {
104
+ return [{
105
+ type: 'SERVER_ERROR',
106
+ error: `Failed to parse server response:\n${resBody}`,
107
+ }, void 0];
108
+ }
109
+ }
110
+ /**
111
+ * Returns the gateway address for the current environment
112
+ */
113
+ get gatewayAddress() {
114
+ return ENVIRONMENT_CONFIGS[this.config.environment].gatewayAddress;
115
+ }
116
+ /**
117
+ * Returns the proxy public key for the current environment
118
+ */
119
+ get proxyPublicKey() {
120
+ return ENVIRONMENT_CONFIGS[this.config.environment].proxyPublicKey;
121
+ }
122
+ /**
123
+ * Fetches a signed nonce from the backend for the given signer address
124
+ * @param addr - signer address
125
+ * @param init - request initialization options
126
+ * @returns a {@link ResponseWrapper `ResponseWrapper`}{@link NonceResponse `<NonceResponse>`}
127
+ */
128
+ async nonce(addr, init = {}) {
129
+ return await this.#get('/nonce', {
130
+ address: addr,
131
+ }, init);
132
+ }
133
+ /**
134
+ * Authenticates a signer with the backend
135
+ * @param auth - authentication request
136
+ * @param init - request initialization options
137
+ * @returns a {@link ResponseWrapper `ResponseWrapper`}{@link AuthResponse `<AuthResponse>`}
138
+ */
139
+ async authenticate(auth, init = {}) {
140
+ return await this.#post('/authenticate', auth, init);
141
+ }
142
+ /**
143
+ * Fetches a signed quote from the backend
144
+ * @param quote - quote request
145
+ * @param init - request initialization options
146
+ * @returns a {@link ResponseWrapper `ResponseWrapper`}{@link QuoteResponse `<QuoteResponse>`}
147
+ */
148
+ async quote(quote, init = {}) {
149
+ return await this.#post('/quote', quote, init);
150
+ }
151
+ /**
152
+ * Creates a SilentSwap order, a prerequisite to depositing funds into the Gateway contract
153
+ * @param order - order request
154
+ * @param init - request initialization options
155
+ * @returns a {@link ResponseWrapper `ResponseWrapper`}{@link OrderResponse `<OrderResponse>`}
156
+ */
157
+ async order(order, init = {}) {
158
+ return await this.#post('/order', order, init);
159
+ }
160
+ }
161
+ /**
162
+ * Creates a new SilentSwap client
163
+ * @param gc_client - client configuration
164
+ * @returns a new {@link SilentSwapClient}
165
+ */
166
+ export function createSilentSwapClient(gc_client) {
167
+ return new SilentSwapClient(gc_client);
168
+ }
@@ -0,0 +1,14 @@
1
+ import { type Hex } from 'viem';
2
+ export declare const EVM_PHONY_ADDRESS: Hex;
3
+ export declare const EVM_NATIVE_ADDRESS: Hex;
4
+ export declare const MAINNET_SILENTSWAP_API = "https://api.silentswap.com/v2";
5
+ export declare const MAINNET_GATEWAY_ADDRESS = "0xA798d4D04faad17C309127C2B9B99Cc459635eDC";
6
+ export declare const MAINNET_GATEWAY_PUBLIC_KEY = "0x0494913354a94d40604cd453d2a654489aa78472daf953c63cae4ceaaa40740d682905847e99618c34b360d8ef4645a954cc9686619995465dfdba6da34f35642f";
7
+ export declare const COIN_TYPES: {
8
+ readonly ETH: "60";
9
+ readonly ATOM: "118";
10
+ readonly SCRT: "529";
11
+ };
12
+ export declare enum ENVIRONMENT {
13
+ MAINNET = "MAINNET"
14
+ }
@@ -0,0 +1,19 @@
1
+ // EVM addresses
2
+ export const EVM_PHONY_ADDRESS = '0x1111111111111111111111111111111111111111';
3
+ export const EVM_NATIVE_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';
4
+ // Mainnet constants
5
+ export const MAINNET_SILENTSWAP_API = 'https://api.silentswap.com/v2';
6
+ export const MAINNET_GATEWAY_ADDRESS = '0xA798d4D04faad17C309127C2B9B99Cc459635eDC';
7
+ export const MAINNET_GATEWAY_PUBLIC_KEY = '0x0494913354a94d40604cd453d2a654489aa78472daf953c63cae4ceaaa40740d682905847e99618c34b360d8ef4645a954cc9686619995465dfdba6da34f35642f';
8
+ // Coin types
9
+ export const COIN_TYPES = {
10
+ ETH: '60',
11
+ ATOM: '118',
12
+ SCRT: '529',
13
+ };
14
+ // Environments
15
+ export var ENVIRONMENT;
16
+ (function (ENVIRONMENT) {
17
+ ENVIRONMENT["MAINNET"] = "MAINNET";
18
+ })(ENVIRONMENT || (ENVIRONMENT = {}));
19
+ ;
@@ -0,0 +1,50 @@
1
+ import { Account, Hex } from 'viem';
2
+ import { OfflineAminoSigner } from '@cosmjs/amino';
3
+ import { FacilitatorKeyType, type FacilitatorPublicKey } from './order.js';
4
+ import type { EncryptSecretKeyArgs, ExportedFacilitator } from './types/sdk.js';
5
+ import type { CoinTypeStr } from './types/core.js';
6
+ import type { ProxyAuthInstruction } from './types/authorization.js';
7
+ export declare const SECP256K1: import("@solar-republic/wasm-secp256k1").Secp256k1;
8
+ export declare class FacilitatorAccount {
9
+ #private;
10
+ /**
11
+ * Creates a facilitator account
12
+ * @param secretKeyBytes optional secret key bytes
13
+ */
14
+ constructor(secretKeyBytes?: Uint8Array<ArrayBuffer>);
15
+ /**
16
+ * Exports the secret key
17
+ * @returns A copy of the secret key
18
+ */
19
+ exportSecretKey(): Uint8Array;
20
+ /**
21
+ * Exports the public key for the given coin type and key type
22
+ * @param coinType coin type
23
+ * @param keyType key type
24
+ * @returns the public key
25
+ */
26
+ exportPublicKey(coinType: CoinTypeStr, keyType: FacilitatorKeyType): FacilitatorPublicKey;
27
+ /**
28
+ * Returns a Viem {@link Account} instance for signing EVM transactions from this account
29
+ * @returns A Promise that resolves to the signer
30
+ */
31
+ evmSigner(): Promise<Account>;
32
+ /**
33
+ * Returns a CosmJS {@link OfflineAminoSigner} instance for signing Cosmos transactions from this account
34
+ * @param bech32Prefix - the bech32 prefix for the desired chain
35
+ * @returns A Promise that resolves to the signer
36
+ */
37
+ cosmosSigner(bech32Prefix: string): Promise<OfflineAminoSigner>;
38
+ /**
39
+ * Signs a proxy authorization instruction
40
+ * @param authorization the proxy authorization instruction to sign
41
+ * @returns the signature
42
+ */
43
+ signProxyAuthorization(authorization: ProxyAuthInstruction): Promise<Hex>;
44
+ /**
45
+ * Encrypts the secret key with the given public key
46
+ * @param args encryption arguments
47
+ * @returns the encrypted secret key
48
+ */
49
+ encryptSecretKeyForProxy(args: EncryptSecretKeyArgs): Promise<ExportedFacilitator>;
50
+ }
@@ -0,0 +1,108 @@
1
+ import { bytesToHex, stringToBytes } from 'viem';
2
+ import { toBase64 } from '@cosmjs/encoding';
3
+ import { initWasmSecp256k1 } from '@solar-republic/wasm-secp256k1';
4
+ import { privateKeyToAccount } from 'viem/accounts';
5
+ import { Secp256k1Wallet } from '@cosmjs/amino';
6
+ import { normalizeBytesish, normalizeJson } from './util.js';
7
+ import { FacilitatorKeyType } from './order.js';
8
+ // wasm-secp256k1 instance
9
+ export const SECP256K1 = await initWasmSecp256k1();
10
+ export class FacilitatorAccount {
11
+ #secretKey;
12
+ /**
13
+ * Creates a facilitator account
14
+ * @param secretKeyBytes optional secret key bytes
15
+ */
16
+ constructor(secretKeyBytes) {
17
+ // if secret was given, copy its bytes into a new section of memory so parent can zeroize original
18
+ this.#secretKey = secretKeyBytes ? secretKeyBytes.slice() : SECP256K1.gen_sk();
19
+ }
20
+ /**
21
+ * Exports the secret key
22
+ * @returns A copy of the secret key
23
+ */
24
+ exportSecretKey() {
25
+ return this.#secretKey.slice();
26
+ }
27
+ /**
28
+ * Exports the public key for the given coin type and key type
29
+ * @param coinType coin type
30
+ * @param keyType key type
31
+ * @returns the public key
32
+ */
33
+ exportPublicKey(coinType, keyType) {
34
+ let publicKeyBytes;
35
+ switch (keyType) {
36
+ case FacilitatorKeyType.SECP256K1:
37
+ publicKeyBytes = SECP256K1.sk_to_pk(this.#secretKey, true);
38
+ break;
39
+ // case FacilitatorKeyType.ED25519:
40
+ // publicKeyBytes =
41
+ // break;
42
+ default:
43
+ throw new Error('Invalid key type');
44
+ }
45
+ return {
46
+ coinType,
47
+ keyType,
48
+ publicKeyBytes: bytesToHex(publicKeyBytes),
49
+ };
50
+ }
51
+ /**
52
+ * Returns a Viem {@link Account} instance for signing EVM transactions from this account
53
+ * @returns A Promise that resolves to the signer
54
+ */
55
+ evmSigner() {
56
+ return Promise.resolve(privateKeyToAccount(bytesToHex(this.#secretKey)));
57
+ }
58
+ /**
59
+ * Returns a CosmJS {@link OfflineAminoSigner} instance for signing Cosmos transactions from this account
60
+ * @param bech32Prefix - the bech32 prefix for the desired chain
61
+ * @returns A Promise that resolves to the signer
62
+ */
63
+ async cosmosSigner(bech32Prefix) {
64
+ return await Secp256k1Wallet.fromKey(this.#secretKey, bech32Prefix);
65
+ }
66
+ /**
67
+ * Signs a proxy authorization instruction
68
+ * @param authorization the proxy authorization instruction to sign
69
+ * @returns the signature
70
+ */
71
+ async signProxyAuthorization(authorization) {
72
+ // create local account
73
+ const account = privateKeyToAccount(bytesToHex(this.#secretKey));
74
+ // sign message
75
+ return await account.signMessage({
76
+ message: bytesToHex(stringToBytes(JSON.stringify(normalizeJson(authorization.content)))),
77
+ });
78
+ }
79
+ /**
80
+ * Encrypts the secret key with the given public key
81
+ * @param args encryption arguments
82
+ * @returns the encrypted secret key
83
+ */
84
+ async encryptSecretKeyForProxy(args) {
85
+ // import secret key
86
+ const privKey = await crypto.subtle.importKey('raw', this.#secretKey, { name: 'AES-GCM' }, true, ['encrypt']);
87
+ // normalize public key
88
+ const proxyPubKey = normalizeBytesish(args.proxyPublicKey);
89
+ // generate key pair for encryption
90
+ const encPrivKey = SECP256K1.gen_sk();
91
+ const encPubKey = SECP256K1.sk_to_pk(encPrivKey);
92
+ // derive shared secret
93
+ const sharedSecret = SECP256K1.ecdh(encPrivKey, proxyPubKey);
94
+ // zeroize secret key bytes
95
+ encPrivKey.fill(0);
96
+ // import secret as wrapping key
97
+ const ecdhPrivKey = await crypto.subtle.importKey('raw', sharedSecret, 'AES-KW', false, ['wrapKey']);
98
+ // zeroize shared secret bytes
99
+ sharedSecret.fill(0);
100
+ // encrypt secret key
101
+ const wrappedKey = await crypto.subtle.wrapKey('raw', privKey, ecdhPrivKey, 'AES-KW');
102
+ // export
103
+ return {
104
+ wrappedKey: toBase64(new Uint8Array(wrappedKey)),
105
+ encryptionPublicKey: toBase64(encPubKey),
106
+ };
107
+ }
108
+ }