Skip to content

Commit 25fba47

Browse files
committed
chore: init adding version param
1 parent 2540e69 commit 25fba47

File tree

5 files changed

+302
-35
lines changed

5 files changed

+302
-35
lines changed

packages/sdk/src/client.ts

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ export class MoneriumClient {
4949
#env: Environment;
5050

5151
#authorizationHeader?: string;
52+
53+
#version?: string;
5254
/**
5355
* The PKCE code verifier
5456
* @deprecated, use localStorage, will be removed in v3
@@ -91,6 +93,7 @@ export class MoneriumClient {
9193
// No arguments, default to sandbox
9294
if (!envOrOptions) {
9395
this.#env = MONERIUM_CONFIG.environments['sandbox'];
96+
this.#version = 'v2';
9497
return;
9598
}
9699
// String argument
@@ -99,6 +102,7 @@ export class MoneriumClient {
99102
} else {
100103
this.#env =
101104
MONERIUM_CONFIG.environments[envOrOptions.environment || 'sandbox'];
105+
this.#version = envOrOptions?.version || 'v2';
102106

103107
if (!isServer) {
104108
const { clientId, redirectUri } =
@@ -348,15 +352,42 @@ export class MoneriumClient {
348352
* {@link https://monerium.dev/api-docs#operation/profile-addresses}
349353
* @category Accounts
350354
*/
351-
linkAddress(profileId: string, body: LinkAddress): Promise<LinkedAddress> {
352-
body = mapChainIdToChain(body);
353-
body.accounts = body.accounts.map((account) => mapChainIdToChain(account));
355+
linkAddress(
356+
body: Omit<LinkAddress, 'accounts'> & { profile: string }
357+
): Promise<LinkedAddress>;
358+
/** @deprecated this function should only take one parameter, body with profile id included */
359+
linkAddress(profileId: string, body: LinkAddress): Promise<LinkedAddress>;
360+
361+
linkAddress(
362+
profileIdOrBody:
363+
| string
364+
| (Omit<LinkAddress, 'accounts'> & { profile: string }),
365+
/** @deprecated this function should only take one parameter, body with profile id included */
366+
body?: LinkAddress
367+
): Promise<LinkedAddress> {
368+
if (typeof profileIdOrBody === 'string' && body && this.#version === 'v1') {
369+
body = mapChainIdToChain(body);
370+
if (body?.accounts) {
371+
body.accounts = body?.accounts.map((account) =>
372+
mapChainIdToChain(account)
373+
);
374+
}
354375

355-
return this.#api(
356-
'post',
357-
`profiles/${profileId}/addresses`,
358-
JSON.stringify(body)
359-
);
376+
return this.#api<LinkedAddress>(
377+
'post',
378+
`profiles/${profileIdOrBody}/addresses`,
379+
JSON.stringify(body)
380+
);
381+
} else if (typeof profileIdOrBody === 'object' && this.#version === 'v2') {
382+
profileIdOrBody = mapChainIdToChain(profileIdOrBody);
383+
return this.#api<LinkedAddress>(
384+
'post',
385+
`addresses`,
386+
JSON.stringify(profileIdOrBody)
387+
);
388+
} else {
389+
throw new Error('Invalid arguments');
390+
}
360391
}
361392

362393
/**
@@ -397,16 +428,32 @@ export class MoneriumClient {
397428
body?: BodyInit | Record<string, string>,
398429
isFormEncoded?: boolean
399430
): Promise<T> {
431+
const headers: Record<string, string> = {
432+
Authorization: this.#authorizationHeader || '',
433+
'Content-Type': `application/${
434+
isFormEncoded ? 'x-www-form-urlencoded' : 'json'
435+
}`,
436+
};
437+
if (this.#version === 'v2') {
438+
headers['Accept'] = 'application/vnd.monerium.api-v2+json';
439+
}
440+
console.log(
441+
'%c headers',
442+
'color:white; padding: 30px; background-color: darkgreen',
443+
headers
444+
);
445+
446+
console.log(
447+
'%c ${this.#env.api}/${resource}',
448+
'color:white; padding: 30px; background-color: darkgreen',
449+
`${this.#env.api}/${resource}`
450+
);
451+
400452
return rest<T>(
401453
`${this.#env.api}/${resource}`,
402454
method,
403455
isFormEncoded ? urlEncoded(body as Record<string, string>) : body,
404-
{
405-
Authorization: this.#authorizationHeader || '',
406-
'Content-Type': `application/${
407-
isFormEncoded ? 'x-www-form-urlencoded' : 'json'
408-
}`,
409-
}
456+
headers
410457
);
411458
}
412459

packages/sdk/src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,7 @@ export type MoneriumEventListener = (notification: OrderNotification) => void;
473473

474474
export type ClassOptions = {
475475
environment?: ENV;
476+
version?: 'v1' | 'v2';
476477
} & BearerTokenCredentials;
477478

478479
export interface AuthFlowOptions {
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
/**
2+
* @jest-environment node
3+
*/
4+
5+
6+
// Password: Passw0rd!
7+
8+
// punkWallet: https://punkwallet.io/pk#0x30fa9f64fb85dab6b4bf045443e08315d6570d4eabce7c1363acda96042a6e1a
9+
10+
import 'jest-localstorage-mock';
11+
12+
import constants from '../src/constants';
13+
import { MoneriumClient } from '../src/index';
14+
import {
15+
Currency,
16+
CurrencyAccounts,
17+
Individual,
18+
Order,
19+
PaymentStandard,
20+
} from '../src/types';
21+
import { rfc3339 } from '../src/utils';
22+
import {
23+
APP_ONE_CREDENTIALS_CLIENT_ID,
24+
APP_ONE_CREDENTIALS_SECRET,
25+
APP_ONE_OWNER_USER_ID,
26+
DEFAULT_PROFILE,
27+
OWNER_SIGNATURE,
28+
PUBLIC_KEY,
29+
} from './constants.js';
30+
31+
const { LINK_MESSAGE } = constants;
32+
33+
const message = LINK_MESSAGE;
34+
35+
let client: MoneriumClient;
36+
37+
// Can't run in CI because of Cloudflare
38+
process.env.CI !== 'true' &&
39+
beforeAll(async () => {
40+
client = new MoneriumClient({
41+
clientId: APP_ONE_CREDENTIALS_CLIENT_ID,
42+
clientSecret: APP_ONE_CREDENTIALS_SECRET,
43+
// version: 'v2',
44+
});
45+
try {
46+
await client.getAccess();
47+
} catch (error) {
48+
console.error('Error, could not authenticate');
49+
}
50+
});
51+
52+
process.env.CI !== 'true' &&
53+
describe('MoneriumClient', () => {
54+
test('authenticate with client credentials', async () => {
55+
const authContext = await client.getAuthContext();
56+
57+
expect(authContext.userId).toBe(APP_ONE_OWNER_USER_ID);
58+
});
59+
// TODO:
60+
// something off with this endpoint
61+
test.skip('link address', async () => {
62+
const authContext = await client.getAuthContext();
63+
64+
const res = await client.linkAddress({
65+
profile: authContext.defaultProfile as string,
66+
address: PUBLIC_KEY,
67+
message: message,
68+
chain: 11155111,
69+
signature: OWNER_SIGNATURE,
70+
});
71+
72+
expect(res).toMatchObject({
73+
address: '0xBd78A5C7efBf7f84C75ef638689A006512E1A6c4',
74+
id: 'ebec4eed-6dcb-11ee-8aa6-5273f65ed05b',
75+
message: 'I hereby declare that I am the address owner.',
76+
meta: {
77+
linkedBy: '9fdfd981-6dca-11ee-8aa6-5273f65ed05b',
78+
},
79+
profile: '9fdfd8f1-6dca-11ee-8aa6-5273f65ed05b',
80+
});
81+
});
82+
83+
test('get profile', async () => {
84+
const authContext = await client.getAuthContext();
85+
const profile = await client.getProfile(
86+
authContext.defaultProfile as string
87+
);
88+
89+
expect(profile.id).toBe(authContext.defaultProfile);
90+
});
91+
92+
test('get balances', async () => {
93+
const balances = await client.getBalances();
94+
95+
expect(balances).toEqual(
96+
expect.arrayContaining([
97+
expect.objectContaining({
98+
// id: '4b208818-44e3-11ed-adac-b2efc0e6677d',
99+
chain: 'ethereum',
100+
address: PUBLIC_KEY,
101+
}),
102+
])
103+
);
104+
}, 15000);
105+
106+
test('get orders', async () => {
107+
const orders = await client.getOrders();
108+
const order = orders.find((o: Order) => o.memo === 'UNIT-TEST') as Order;
109+
110+
expect(order.kind).toBe('redeem');
111+
expect((order.counterpart.details as Individual).firstName).toBe('Test');
112+
expect((order.counterpart.details as Individual).lastName).toBe(
113+
'Testerson'
114+
);
115+
expect(order.amount).toBe('1.33');
116+
expect(order.memo).toBe('UNIT-TEST');
117+
});
118+
119+
test('get orders by profileId', async () => {
120+
const orders = await client.getOrders({
121+
profile: DEFAULT_PROFILE,
122+
});
123+
124+
orders.map((o: Order) => {
125+
expect(DEFAULT_PROFILE).toBe(o.profile);
126+
});
127+
});
128+
129+
test('get order', async () => {
130+
const order = await client.getOrder(
131+
'7859502a-4b5d-11ef-88d4-46da5b198e23'
132+
);
133+
134+
expect(order.kind).toBe('redeem');
135+
expect(order.amount).toBe('1.33');
136+
expect(order.memo).toBe('UNIT-TEST');
137+
});
138+
139+
test('get tokens', async () => {
140+
const tokens = await client.getTokens();
141+
142+
const expected = [
143+
{
144+
address: '0x67b34b93ac295c985e856E5B8A20D83026b580Eb',
145+
chain: 'ethereum',
146+
currency: 'eur',
147+
decimals: 18,
148+
symbol: 'EURe',
149+
ticker: 'EUR',
150+
},
151+
];
152+
expect(tokens).toEqual(expect.arrayContaining(expected));
153+
});
154+
155+
// there is no way to test this without a real time signature, the date is now verified
156+
test('place order signature error', async () => {
157+
const date = new Date().toISOString();
158+
const rfc3339date = rfc3339(new Date(date));
159+
160+
const placeOrderMessage = `Send EUR 10 to GR1601101250000000012300695 at ${rfc3339date}`;
161+
const placeOrderSignatureHash =
162+
'0x23bf7e1b240d238b13cb293673c3419915402bb34435af62850b1d8e63f82c564fb73ab19691cf248594423dd01e441bb2ccb38ce2e2ecc514dfc3075bea829e1c';
163+
164+
await client
165+
.placeOrder({
166+
amount: '10',
167+
signature: placeOrderSignatureHash,
168+
currency: Currency.eur,
169+
address: PUBLIC_KEY,
170+
counterpart: {
171+
identifier: {
172+
standard: PaymentStandard.iban,
173+
iban: 'GR1601101250000000012300695',
174+
},
175+
details: {
176+
firstName: 'Mockbank',
177+
lastName: 'Testerson',
178+
},
179+
},
180+
message: placeOrderMessage,
181+
memo: 'Powered by Monerium SDK',
182+
chain: 11155111,
183+
})
184+
.catch((err) => {
185+
expect(err.errors?.signature).toBe('invalid signature');
186+
});
187+
});
188+
189+
test('place order timestamp error', async () => {
190+
const date = 'Thu, 29 Dec 2022 14:58 +00:00';
191+
const placeOrderMessage = `Send EUR 10 to GR1601101250000000012300695 at ${date}`;
192+
const placeOrderSignatureHash =
193+
'0x23bf7e1b240d238b13cb293673c3419915402bb34435af62850b1d8e63f82c564fb73ab19691cf248594423dd01e441bb2ccb38ce2e2ecc514dfc3075bea829e1c';
194+
195+
await client
196+
.placeOrder(
197+
{
198+
amount: '10',
199+
signature: placeOrderSignatureHash,
200+
currency: Currency.eur,
201+
address: PUBLIC_KEY,
202+
counterpart: {
203+
identifier: {
204+
standard: PaymentStandard.iban,
205+
iban: 'GR1601101250000000012300695',
206+
},
207+
details: {
208+
firstName: 'Mockbank',
209+
lastName: 'Testerson',
210+
},
211+
},
212+
message: placeOrderMessage,
213+
memo: 'Powered by Monerium SDK',
214+
chain: 'ethereum',
215+
} as any /** to bypass typeerror for chain and network */
216+
)
217+
.catch((err) => {
218+
expect(err.errors?.message).toBe('timestamp is expired');
219+
});
220+
});
221+
});
222+
// TODO:
223+
// test("upload supporting document", async () => {
224+
225+
// // const document = client.uploadSupportingDocument();
226+
// // assertObjectMatch(document, {});
227+
// });
228+
229+
process.env.CI === 'true' &&
230+
it('SKIPPED', () => {
231+
expect(true).toBe(true);
232+
});

packages/sdk/test/client-server.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ process.env.CI !== 'true' &&
4040
client = new MoneriumClient({
4141
clientId: APP_ONE_CREDENTIALS_CLIENT_ID,
4242
clientSecret: APP_ONE_CREDENTIALS_SECRET,
43+
version: 'v1',
4344
});
4445
try {
4546
await client.getAccess();

0 commit comments

Comments
 (0)