@t-0/provider-starter-ts 0.1.0

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.
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "{{PROJECT_NAME}}",
3
+ "version": "1.0.0",
4
+ "description": "Node.js service built with your SDK",
5
+ "main": "dist/index.js",
6
+ "scripts": {
7
+ "dev": "ts-node src/index.ts",
8
+ "build": "tsc",
9
+ "start": "node dist/index.js",
10
+ "lint": "eslint src --ext .ts",
11
+ "test": "echo \"Error: no test specified\" && exit 1"
12
+ },
13
+ "keywords": [],
14
+ "author": "",
15
+ "license": "MIT",
16
+ "dependencies": {
17
+ "@t-0/provider-sdk": "1.0.33",
18
+ "dotenv": "^16.3.1",
19
+ "tiny-invariant": "^1.3.3"
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^20.10.0",
23
+ "@typescript-eslint/eslint-plugin": "^6.12.0",
24
+ "@typescript-eslint/parser": "^6.12.0",
25
+ "eslint": "^8.54.0",
26
+
27
+ "ts-node": "^10.9.1",
28
+ "typescript": "^5.3.2"
29
+ }
30
+ }
@@ -0,0 +1,29 @@
1
+ import {type Client, NetworkService, PaymentMethodType, QuoteType} from "@t-0/provider-sdk";
2
+ import {fromProtoDecimal, toProtoDecimal} from "./lib";
3
+
4
+ export default async function getQuote(networkClient: Client<typeof NetworkService>) {
5
+ const eurgbp = await networkClient.getQuote({
6
+ payInCurrency: "EUR",
7
+ payInMethod: PaymentMethodType.SEPA,
8
+ payOutCurrency: "GBP",
9
+ payOutMethod: PaymentMethodType.SWIFT,
10
+ quoteType: QuoteType.REALTIME,
11
+ amount: {
12
+ amount: {
13
+ case: 'payInAmount',
14
+ value: toProtoDecimal(1000, 0)
15
+ },
16
+ }
17
+ })
18
+
19
+ switch (eurgbp.result.case) {
20
+ case 'success':
21
+ console.log(`EUR/GBP: ${fromProtoDecimal(eurgbp.result.value.rate!)}`)
22
+ break;
23
+ case 'failure':
24
+ console.error(eurgbp.result.value.reason)
25
+ break;
26
+ default:
27
+ console.error("unexpected result type")
28
+ }
29
+ }
@@ -0,0 +1,67 @@
1
+ import dotenv from 'dotenv';
2
+ import {
3
+ createClient,
4
+ createService,
5
+ NetworkService,
6
+ nodeAdapter,
7
+ ProviderService,
8
+ signatureValidation,
9
+ } from "@t-0/provider-sdk";
10
+ import invariant from 'tiny-invariant';
11
+ import http from "http";
12
+ import publishQuotes from "./publish_quotes";
13
+ import CreateProviderService from "./service";
14
+ import getQuote from "./get_quote";
15
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
16
+ import submitPayment from "./submit_payment";
17
+
18
+ dotenv.config();
19
+
20
+ const privateKeyHex = process.env.PROVIDER_PRIVATE_KEY;
21
+ const port = process.env.PORT || 3000;
22
+ const endpoint = process.env.TZERO_ENDPOINT || "https://api-sandbox.t-0.network";
23
+ const quotePublishingInterval: number = Number(process.env.QUOTE_PUBLISHING_INTERVAL || "5000");
24
+ const networkPublicKeyHex = process.env.NETWORK_PUBLIC_KEY;
25
+
26
+ invariant(privateKeyHex, 'Private key not set');
27
+ invariant(quotePublishingInterval > 0, 'Interval must be positive');
28
+ invariant(networkPublicKeyHex, 'Network public key is not set');
29
+
30
+ async function main() {
31
+ console.log('🚀 Service starting...');
32
+ console.log(`📡 Port: ${port}`);
33
+ const networkClient = createClient(privateKeyHex!, endpoint, NetworkService);
34
+
35
+ await publishQuotes(networkClient, quotePublishingInterval)
36
+
37
+ const server = http.createServer(
38
+ signatureValidation(
39
+ nodeAdapter(
40
+ createService(networkPublicKeyHex!, (r) => {
41
+ r.service(ProviderService, CreateProviderService());
42
+ })))
43
+ ).listen(port);
44
+ console.log("✅ Service ready and is listening at", server.address());
45
+
46
+ // Step 1.1 is done. You successfully initialised starter template
47
+
48
+ // TODO: Step 1.2 take you generated public key from .env and share it with t-0 team
49
+
50
+ // TODO: Step 1.3 implement publishing of quotes in the ./publish_quotes.ts
51
+
52
+ // TODO: Step 1.4 check that quote for target currency is successfully received
53
+ await getQuote(networkClient)
54
+
55
+ // TODO: Step 2.2 deploy your integration and provide t-0 team base URL of your deployment
56
+
57
+ // TODO: Step 2.3 check that you can submit payment by revisiting ./submit_payment.ts and uncommenting following line
58
+ // await submitPayment(networkClient)
59
+
60
+ // TODO: Step 2.5 ask t-0 team to submit a payment which would trigger your payOut endpoint
61
+ }
62
+
63
+ main().catch((error) => {
64
+ console.error('❌ Error starting service:', error);
65
+ process.exit(1);
66
+ });
67
+
@@ -0,0 +1,13 @@
1
+ import {Decimal, DecimalSchema} from "@t-0/provider-sdk";
2
+ import {create} from "@bufbuild/protobuf";
3
+
4
+ export const toProtoDecimal = (unscaled: number, exponent: number): Decimal => {
5
+ return create(DecimalSchema, {
6
+ unscaled: BigInt(unscaled),
7
+ exponent: exponent,
8
+ });
9
+ }
10
+
11
+ export const fromProtoDecimal = (value: Decimal): number => {
12
+ return Number(value.unscaled) * Math.pow(10, value.exponent)
13
+ }
@@ -0,0 +1,34 @@
1
+ import {type Client, NetworkService, PaymentMethodType, QuoteType} from "@t-0/provider-sdk";
2
+ import {toProtoDecimal} from "./lib";
3
+ import {randomUUID} from "node:crypto";
4
+ import {timestampFromDate} from "@bufbuild/protobuf/wkt";
5
+
6
+ export default async function publishQuotes(networkClient: Client<typeof NetworkService>, quotePublishingInterval: number): Promise<void> {
7
+ // TODO: Step 1.3 replace this with receiving quotes from you systems and publishing them into t-0 Network. We recommend publishing at least once per 5 seconds, but not more than once per second
8
+ const tick = async () => {
9
+ try {
10
+ await networkClient.updateQuote({
11
+ payIn: [{
12
+ bands: [{
13
+ // note that rate is always USD/XXX, os that for EUR quote should be USD/EUR
14
+ rate: toProtoDecimal(863, -3), // rate 0.863
15
+ maxAmount: toProtoDecimal(1000, 0), // maximum amount in USD, could be 1000,5000,10000 or 25000
16
+ clientQuoteId: randomUUID(),
17
+ }],
18
+ currency: 'EUR',
19
+ expiration: timestampFromDate(new Date(Date.now() + 30 * 1000)), // expiration time (30 seconds from now)
20
+ quoteType: QuoteType.REALTIME, // REALTIME is only one supported right now
21
+ paymentMethod: PaymentMethodType.SEPA,
22
+ timestamp: timestampFromDate(new Date()), // Current timestamp
23
+ }]
24
+ })
25
+ } catch (error) {
26
+ console.error(error);
27
+ return
28
+ }
29
+ console.log("quote published")
30
+ }
31
+
32
+ await tick()
33
+ setInterval(tick, quotePublishingInterval);
34
+ }
@@ -0,0 +1,50 @@
1
+ import {
2
+ PayoutRequest,
3
+ PayoutResponse,
4
+ UpdatePaymentRequest,
5
+ UpdateLimitRequest,
6
+ UpdateLimitResponse,
7
+ AppendLedgerEntriesRequest,
8
+ AppendLedgerEntriesResponse,
9
+ UpdatePaymentResponse,
10
+ HandlerContext,
11
+ } from "@t-0/provider-sdk";
12
+
13
+ /*
14
+ Please refer to docs, proto definition comments or source code comments to understand purpose of fields
15
+ */
16
+ const CreateProviderService = () => {
17
+ return {
18
+ async updatePayment(req: UpdatePaymentRequest, _: HandlerContext) {
19
+ // TODO: Step 2.1 implement how you handle updates of payment initiated by you
20
+ console.log(`Received payment update for ${req.paymentId}, payment ${req.result.case}`)
21
+ return {} as UpdatePaymentResponse
22
+ },
23
+
24
+ async payOut(req: PayoutRequest, _: HandlerContext) {
25
+ // TODO: Step 2.4 implement how you do payouts (payments initiated by your counterparts)
26
+ console.log(`Received payout request ${req.payoutId}`)
27
+ return {
28
+ result: {
29
+ case: "accepted",
30
+ value: {},
31
+ },
32
+ } as PayoutResponse
33
+ },
34
+
35
+ async updateLimit(req: UpdateLimitRequest, _: HandlerContext) {
36
+ // TODO: optionally implement updates on your limits and limits usage
37
+ console.log(`Received update of limits with provider ${req.limits[0].creditorId}`)
38
+ return {} as UpdateLimitResponse
39
+ },
40
+
41
+ async appendLedgerEntries(req: AppendLedgerEntriesRequest, _: HandlerContext) {
42
+ // TODO: optionally implement handling of new ledger transactions and new ledger entries
43
+ console.log(`Received ledger entries for ${req.transactions} transaction(s)`)
44
+ return {
45
+ } as AppendLedgerEntriesResponse
46
+ },
47
+ }
48
+ };
49
+
50
+ export default CreateProviderService;
@@ -0,0 +1,24 @@
1
+ import {type Client, NetworkService, PaymentMethodType} from "@t-0/provider-sdk";
2
+
3
+ export default async function submitPayment(networkClient: Client<typeof NetworkService>): Promise<void> {
4
+ // TODO: Step 2.3 replace this with receiving quotes from you systems and publishing them into t-0 Network. We recommend publishing at least once per 5 seconds, but not more than once per second
5
+ await networkClient.createPayment({
6
+ payIn: {
7
+ currency: 'EUR',
8
+ paymentMethod: PaymentMethodType.SEPA,
9
+ },
10
+ payOut: {
11
+ currency: 'GBP',
12
+ paymentDetails: {
13
+ details: {
14
+ case: "sepa",
15
+ value: {
16
+ iban: 'GB12345567890',
17
+ beneficiaryName: 'Max Mustermann',
18
+ }
19
+ }
20
+ }
21
+ }
22
+
23
+ })
24
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "commonjs",
5
+ "lib": ["ES2020"],
6
+ "outDir": "./dist",
7
+ "rootDir": "./src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "resolveJsonModule": true,
13
+ "declaration": true,
14
+ "declarationMap": true,
15
+ "sourceMap": true,
16
+ "moduleResolution": "node"
17
+ },
18
+ "include": ["src/**/*"],
19
+ "exclude": ["node_modules", "dist"]
20
+ }