Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/minimal-token/daml.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
sdk-version: 3.3.0-snapshot.20250502.13767.0.v2fc6c7e2
sdk-version: 3.3.0-snapshot.20250507.0
#sdk-version: 3.3.0-snapshot.20250502.13767.0.v2fc6c7e2
name: minimal-token
version: 0.1.0
source: daml
Expand Down
3 changes: 3 additions & 0 deletions packages/token-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
},
"devDependencies": {
"@canton-network/core-ledger-client": "^0.17.0",
"@types/jsonwebtoken": "^9.0.10",
"@types/node": "^22.13.10",
"@typescript/analyze-trace": "^0.10.1",
"@veraswap/esbuild-config": "latest",
Expand All @@ -75,6 +76,8 @@
},
"dependencies": {
"@canton-network/wallet-sdk": "^0.16.0",
"dotenv": "^16.4.7",
"jsonwebtoken": "^9.0.3",
"tweetnacl": "^1.0.3",
"tweetnacl-util": "^0.15.1",
"uuid": "^13.0.0"
Expand Down
27 changes: 27 additions & 0 deletions packages/token-sdk/src/checkDars.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { MINIMAL_TOKEN_PACKAGE_ID } from "./constants/MINIMAL_TOKEN_PACKAGE_ID.js";
import { getDefaultSdkAndConnect } from "./sdkHelpers.js";

export async function checkDars() {
const sdk = await getDefaultSdkAndConnect();
await sdk.connectAdmin();

const isDarUploaded = await sdk.userLedger?.isPackageUploaded(
MINIMAL_TOKEN_PACKAGE_ID
);

if (isDarUploaded) {
console.info("minimal-token DAR already uploaded");
} else {
console.info("minimal-token DAR not uploaded");
}
}

checkDars()
.then(() => {
console.info("Done");
process.exit(0);
})
.catch((error) => {
console.error("Error in checkDars: ", error);
process.exit(1);
});
67 changes: 67 additions & 0 deletions packages/token-sdk/src/helpers/get5NAuthController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { AuthController } from "@canton-network/wallet-sdk";
import jwt from "jsonwebtoken";
import { get5NToken } from "./get5NToken.js";

export const get5NAuthController = ({
clientId,
clientSecret,
audience,
}: {
clientId: string;
clientSecret: string;
audience?: string;
}): AuthController => {
audience = audience ?? clientId;

let userAccessToken: string | undefined = undefined;

const isJwtValid = (token: string): boolean => {
const payload = jwt.decode(token, { json: true });
if (!payload) return false;

const now = Math.floor(Date.now() / 1000);
return typeof payload.exp === "number" && payload.exp > now;
};

return {
userId: clientId,
getUserToken: async () => {
const cachedAccessToken = userAccessToken;
if (cachedAccessToken && isJwtValid(cachedAccessToken)) {
return { userId: clientId, accessToken: cachedAccessToken };
}

const tokenResponse = await get5NToken({
clientId,
clientSecret,
audience,
});

userAccessToken = tokenResponse.access_token;

return {
userId: clientId,
accessToken: tokenResponse.access_token,
};
},
getAdminToken: async () => {
const cachedAccessToken = userAccessToken;
if (cachedAccessToken && isJwtValid(cachedAccessToken)) {
return { userId: clientId, accessToken: cachedAccessToken };
}

const tokenResponse = await get5NToken({
clientId,
clientSecret,
audience,
});

userAccessToken = tokenResponse.access_token;

return {
userId: clientId,
accessToken: tokenResponse.access_token,
};
},
};
};
56 changes: 56 additions & 0 deletions packages/token-sdk/src/helpers/get5NToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
export interface TokenResponse5N {
access_token: string;
token_type: string;
scope: string;
expires_in: number;
id_token: string;
}

export async function get5NToken({
clientId,
clientSecret,
audience,
}: {
clientId: string;
clientSecret: string;
audience?: string;
}) {
if (!clientId || !clientSecret) {
throw new Error(
"CLIENT_ID_5N and CLIENT_SECRET_5N environment variables must be set"
);
}

const url = "https://auth.sandbox.fivenorth.io/application/o/token/";
const headers = {
"Content-Type": "application/x-www-form-urlencoded",
};

const body = new URLSearchParams({
grant_type: "client_credentials",
client_id: clientId,
client_secret: clientSecret,
audience: audience ?? clientId, // Often, audience is the client_id itself for client_credentials
scope: "daml_ledger_api",
}).toString();

try {
const response = await fetch(url, {
method: "POST",
headers: headers,
body: body,
});

if (!response.ok) {
const errorData = await response.text();
throw new Error(
`HTTP error! Status: ${response.status}, Details: ${errorData}`
);
}

return (await response.json()) as TokenResponse5N;
} catch (error) {
console.error("Failed to obtain 5N token:", error);
throw error;
}
}
89 changes: 79 additions & 10 deletions packages/token-sdk/src/sdkHelpers.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,86 @@
import {
Config,
localNetAuthDefault,
localNetLedgerDefault,
localNetTokenStandardDefault,
WalletSDKImpl,
WalletSDK,
AuthTokenProvider,
LedgerController,
TokenStandardController,
ValidatorController,
} from "@canton-network/wallet-sdk";
import { get5NAuthController } from "./helpers/get5NAuthController.js";

import dotenv from "dotenv";

dotenv.config();

export const FIVEN_SCAN_PROXY_API_URL = new URL(
"https://wallet.validator.devnet.sandbox.fivenorth.io/api/validator"
);

export const FIVEN_LEDGER_API_URL = new URL(
"https://ledger-api.validator.devnet.sandbox.fivenorth.io/"
);

export const FIVEN_VALIDATOR_API_URL = new URL(
"https://wallet.validator.devnet.sandbox.fivenorth.io/"
);

const USE_5N = process.env.USE_5N === "true";
const CLIENT_ID_5N = process.env.CLIENT_ID_5N ?? "";
const CLIENT_SECRET_5N = process.env.CLIENT_SECRET_5N ?? "";

const defaultSdkConfig: Config = {
logger: console,
authFactory: localNetAuthDefault,
ledgerFactory: localNetLedgerDefault,
tokenStandardFactory: localNetTokenStandardDefault,
};

const fiveNSdkConfig: Config = {
logger: console,
authFactory: () =>
get5NAuthController({
clientId: CLIENT_ID_5N,
clientSecret: CLIENT_SECRET_5N,
}),
ledgerFactory: (
userId: string,
authTokenProvider: AuthTokenProvider,
isAdmin = false
) =>
new LedgerController(
userId,
FIVEN_LEDGER_API_URL,
undefined,
isAdmin,
authTokenProvider
),
tokenStandardFactory: (
userId: string,
authTokenProvider: AuthTokenProvider
) =>
new TokenStandardController(
userId,
FIVEN_LEDGER_API_URL,
FIVEN_VALIDATOR_API_URL,
undefined,
authTokenProvider
),
validatorFactory: (userId: string, authTokenProvider: AuthTokenProvider) =>
new ValidatorController(
userId,
FIVEN_VALIDATOR_API_URL,
authTokenProvider
),
};

export const getDefaultSdk = () =>
new WalletSDKImpl().configure({
logger: console,
authFactory: localNetAuthDefault,
ledgerFactory: localNetLedgerDefault,
tokenStandardFactory: localNetTokenStandardDefault,
});
new WalletSDKImpl().configure(USE_5N ? fiveNSdkConfig : defaultSdkConfig);

const LOCALNET_SCAN_PROXY_API_URL = new URL(
export const LOCALNET_SCAN_PROXY_API_URL = new URL(
"http://localhost:2000/api/validator"
);

Expand All @@ -29,7 +95,9 @@ export const getSdkForParty = async (partyId: string): Promise<WalletSDK> => {
const sdkPromise = (async () => {
const sdk = getDefaultSdk();
await sdk.connect();
await sdk.connectTopology(LOCALNET_SCAN_PROXY_API_URL);
await sdk.connectTopology(
USE_5N ? FIVEN_SCAN_PROXY_API_URL : LOCALNET_SCAN_PROXY_API_URL
);
await sdk.setPartyId(partyId);
return sdk;
})();
Expand All @@ -46,7 +114,8 @@ export const getSdkForParty = async (partyId: string): Promise<WalletSDK> => {
export const getDefaultSdkAndConnect = async () => {
const sdk = getDefaultSdk();
await sdk.connect();
// await sdk.connectTopology(localNetStaticConfig.LOCALNET_SCAN_PROXY_API_URL);
await sdk.connectTopology(LOCALNET_SCAN_PROXY_API_URL);
await sdk.connectTopology(
USE_5N ? FIVEN_SCAN_PROXY_API_URL : LOCALNET_SCAN_PROXY_API_URL
);
return sdk;
};
40 changes: 40 additions & 0 deletions packages/token-sdk/src/testScripts/createUsers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { getDefaultSdkAndConnect } from "../sdkHelpers.js";

const PRIMARY_PARTY_ID = process.env.PRIMARY_PARTY_ID;

async function createUsers() {
if (!PRIMARY_PARTY_ID) {
throw new Error("PRIMARY_PARTY_ID is not set in environment variables");
}

const sdk = await getDefaultSdkAndConnect();
await sdk.connectAdmin();
const adminLedger = sdk.adminLedger!;

const userAlice = await adminLedger.createUser(
"minimal-token-alice",
PRIMARY_PARTY_ID
);

const userBob = await adminLedger.createUser(
"minimal-token-bob",
PRIMARY_PARTY_ID
);

const userCharlie = await adminLedger.createUser(
"minimal-token-charlie",
PRIMARY_PARTY_ID
);

console.log({ userAlice, userBob, userCharlie });
}

createUsers()
.then(() => {
console.info("Done");
process.exit(0);
})
.catch((error) => {
console.error("Error in createUsers: ", error);
process.exit(1);
});
1 change: 0 additions & 1 deletion packages/token-sdk/src/testScripts/hello.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { getWrappedSdkWithKeyPair } from "../wrappedSdk/wrappedSdk.js";

async function hello() {
const sdk = await getDefaultSdkAndConnect();
await sdk.connectAdmin();

// NOTE: this is of course for testing
const aliceKeyPair = keyPairFromSeed("alice");
Expand Down
25 changes: 25 additions & 0 deletions packages/token-sdk/src/testScripts/print5NToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import dotenv from "dotenv";
import { get5NToken } from "../helpers/get5NToken.js";

dotenv.config();

async function print5NToken() {
const CLIENT_ID_5N = process.env.CLIENT_ID_5N ?? "";
const CLIENT_SECRET_5N = process.env.CLIENT_SECRET_5N ?? "";

const token = await get5NToken({
clientId: CLIENT_ID_5N,
clientSecret: CLIENT_SECRET_5N,
});

console.log(token.access_token);
}

print5NToken()
.then(() => {
process.exit(0);
})
.catch((error) => {
console.error("Error in get5NToken: ", error);
process.exit(1);
});
17 changes: 2 additions & 15 deletions packages/token-sdk/src/uploadDars.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,11 @@
import {
localNetAuthDefault,
localNetLedgerDefault,
localNetTokenStandardDefault,
WalletSDKImpl,
} from "@canton-network/wallet-sdk";
import fs from "fs/promises";
import path from "path";
import { MINIMAL_TOKEN_PACKAGE_ID } from "./constants/MINIMAL_TOKEN_PACKAGE_ID.js";

const sdk = new WalletSDKImpl().configure({
logger: console,
authFactory: localNetAuthDefault,
ledgerFactory: localNetLedgerDefault,
tokenStandardFactory: localNetTokenStandardDefault,
});
import { getDefaultSdkAndConnect } from "./sdkHelpers.js";

export async function uploadDars() {
await sdk.connect();
const sdk = await getDefaultSdkAndConnect();
await sdk.connectAdmin();
await sdk.connectTopology(new URL("http://localhost:2000/api/validator"));

const isDarUploaded = await sdk.userLedger?.isPackageUploaded(
MINIMAL_TOKEN_PACKAGE_ID
Expand Down
Loading
Loading