Skip to content

Commit

Permalink
feat: PoC of the SDK (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
RezaRahemtola authored Aug 9, 2024
1 parent 3cc1dbc commit 155f71d
Show file tree
Hide file tree
Showing 18 changed files with 1,704 additions and 20 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,7 @@ bin
# Dev environment metadata
.idea
.DS_Store
*.log
*.log

# Local development package
/packages/sdk/adena-wallet-sdk-*.tgz
12 changes: 9 additions & 3 deletions packages/sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
"author": "Onbloc, Inc.",
"homepage": "https://www.adena.app",
"main": "./bin/index.js",
"module": "./bin/index.mjs",
"types": "./bin/index.d.ts",
"files": [
"bin/**/*"
],
Expand All @@ -26,12 +28,15 @@
"js"
],
"scripts": {
"build": "yarn tsc",
"lint": "eslint",
"lint:fix": "yarn lint --fix",
"build": "tsup",
"lint": "prettier src/ --check && eslint",
"lint:fix": "prettier src/ --write && eslint --fix",
"test": "jest",
"test:ci": "jest --coverage --passWithNoTests "
},
"dependencies": {
"@gnolang/gno-js-client": "^1.3.0"
},
"devDependencies": {
"@types/eslint": "^9",
"@types/jest": "^29.5.12",
Expand All @@ -44,6 +49,7 @@
"prettier": "^3.3.3",
"ts-jest": "^29.2.3",
"ts-node": "^10.9.2",
"tsup": "^8.2.3",
"typescript": "^5.5.4"
}
}
4 changes: 4 additions & 0 deletions packages/sdk/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { EMessageType } from './types';

export { EMessageType };
export * from './methods';
26 changes: 26 additions & 0 deletions packages/sdk/src/methods/events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { OnAccountChangeFunc, OnEventType, OnNetworkChangeFunc } from '../types/methods/events';
import { getAdena } from '../utils';

/**
* Add a listener on connected account changes
* @async
* @param {OnAccountChangeFunc} func - Function to call on a new event
* @returns Nothing, throws an error if it fails
*/
export const onAccountChange = async (func: OnAccountChangeFunc): Promise<void> => {
const adena = getAdena();

adena.On(OnEventType.CHANGED_ACCOUNT, func);
};

/**
* Add a listener on network changes
* @async
* @param {OnNetworkChangeFunc} func - Function to call on a new event
* @returns Nothing, throws an error if it fails
*/
export const onNetworkChange = async (func: OnNetworkChangeFunc): Promise<void> => {
const adena = getAdena();

adena.On(OnEventType.CHANGED_NETWORK, func);
};
45 changes: 45 additions & 0 deletions packages/sdk/src/methods/general.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { EAdenaResponseStatus } from '../types/common';
import { getAdena } from '../utils';
import { AddEstablishResponse, AddEstablishResponseType, GetAccountResponseData } from '../types/methods/general';

/**
* Establish a connection to your site from Adena
* @async
* @param {string} name - The name of the website requesting to connect
* @returns Original Adena response, useful to check if the site was already connected
*/
export const establishConnection = async (name: string): Promise<AddEstablishResponse> => {
const adena = getAdena();

// Establish a connection to the wallet
const response = await adena.AddEstablish(name);

if (
response.status === EAdenaResponseStatus.SUCCESS &&
(response.type === AddEstablishResponseType.ALREADY_CONNECTED ||
response.type === AddEstablishResponseType.CONNECTION_SUCCESS)
) {
// Adena establishes a connection if:
// - the app was not connected before, and the user approves
// - the app was connected before
return response;
}

throw new Error(`Unable to establish connection: ${response.message}`);
};

/**
* Fetch information about the current connected account
* @async
* @returns Original Adena response with the account information
*/
export const getAccountInfo = async (): Promise<GetAccountResponseData> => {
const adena = getAdena();

const response = await adena.GetAccount();
if (response.status !== EAdenaResponseStatus.SUCCESS) {
throw new Error(`Unable to fetch account info: ${response.message}`);
}

return response.data;
};
4 changes: 4 additions & 0 deletions packages/sdk/src/methods/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { addNetwork, switchNetwork } from './network';
export { signAndSendTransaction, signTransaction } from './transactions';
export { establishConnection, getAccountInfo } from './general';
export { onAccountChange, onNetworkChange } from './events';
45 changes: 45 additions & 0 deletions packages/sdk/src/methods/network.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { getAdena } from '../utils';
import { EAdenaResponseStatus } from '../types/common';
import { SwitchNetworkResponseType } from '../types/methods/network';

/**
* Switches the Adena network to the given chain ID
* @async
* @param {string} chainId - Chain ID
* @returns Nothing, throws an error if it fails
*/
export const switchNetwork = async (chainId: string): Promise<void> => {
const adena = getAdena();

const response = await adena.SwitchNetwork(chainId);
if (
(response.status === EAdenaResponseStatus.SUCCESS &&
response.type === SwitchNetworkResponseType.SWITCH_NETWORK_SUCCESS) ||
(response.status === EAdenaResponseStatus.FAILURE &&
response.type === SwitchNetworkResponseType.REDUNDANT_CHANGE_REQUEST)
) {
return;
}

throw new Error(`Unable to switch Adena network: ${response.message}`);
};

/**
* Add a custom network to Adena
* @async
* @param {string} chainId - Chain ID
* @param {string} chainName - Chain name
* @param {string} rpcUrl - Network RPC URL
* @returns Nothing, throws an error if it fails
*/
export const addNetwork = async (chainId: string, chainName: string, rpcUrl: string): Promise<void> => {
const adena = getAdena();

const response = await adena.AddNetwork({ chainId, chainName, rpcUrl });

if (response.status === EAdenaResponseStatus.SUCCESS) {
return;
}

throw new Error(`Unable to add network ${response.message}`);
};
72 changes: 72 additions & 0 deletions packages/sdk/src/methods/transactions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { BroadcastTxCommitResult } from '@gnolang/tm2-js-client';

import { getAdena } from '../utils';
import { EAdenaResponseStatus } from '../types/common';
import { ContractMessage } from '../types/methods/transactions';

const DEFAULT_GAS_FEE = 1000000;
const DEFAULT_GAS_LIMIT = 5000000;

/**
* Sign, then send a transaction crafted by a web-app
* @async
* @param {ContractMessage[]} messages - Messages to send in the transaction
* @param {number} gasFee - Actual network fee to pay (in ugnot)
* @param {number} gasWanted - Gas limit (in ugnot)
* @param {string} memo - Transaction memo (tag)
* @returns {BroadcastTxCommitResult} Result of the broadcast transaction
*/
export const signAndSendTransaction = async (
messages: ContractMessage[],
gasFee: number = DEFAULT_GAS_FEE,
gasWanted: number = DEFAULT_GAS_LIMIT,
memo?: string
): Promise<BroadcastTxCommitResult> => {
const adena = getAdena();

// Sign and send the transaction
const response = await adena.DoContract({
messages,
gasFee,
gasWanted,
memo,
});

if (response.status !== EAdenaResponseStatus.SUCCESS) {
throw new Error(`Unable to send transaction: ${response.message}`);
}

return response.data;
};

/**
* Sign a transaction crafted by a web-app
* @async
* @param {ContractMessage[]} messages - Messages to send in the transaction
* @param {number} gasFee - Actual network fee to pay (in ugnot)
* @param {number} gasWanted - Gas limit (in ugnot)
* @param {string} memo - Transaction memo (tag)
* @returns {string} Encoded transaction
*/
export const signTransaction = async (
messages: ContractMessage[],
gasFee: number = DEFAULT_GAS_FEE,
gasWanted: number = DEFAULT_GAS_LIMIT,
memo?: string
): Promise<string> => {
const adena = getAdena();

// Sign the transaction
const response = await adena.SignTx({
messages,
gasFee,
gasWanted,
memo,
});

if (response.status !== EAdenaResponseStatus.SUCCESS) {
throw new Error(`Unable to sign transaction ${response.message}`);
}

return response.data.encodedTransaction;
};
12 changes: 12 additions & 0 deletions packages/sdk/src/types/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export enum EAdenaResponseStatus {
SUCCESS = 'success',
FAILURE = 'failure',
}

export interface IAdenaResponse<T, D> {
code: number;
status: EAdenaResponseStatus;
type: T;
message: string;
data: D;
}
28 changes: 28 additions & 0 deletions packages/sdk/src/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { AdenaDoContract, AdenaSignTx } from './methods/transactions';
import { AdenaAddNetwork, AdenaSwitchNetwork } from './methods/network';
import { AdenaAddEstablish, AdenaGetAccount } from './methods/general';
import { AdenaOnEvent } from './methods/events';

export type AdenaWallet = {
// General
AddEstablish: AdenaAddEstablish;
GetAccount: AdenaGetAccount;

// Network
SwitchNetwork: AdenaSwitchNetwork;
AddNetwork: AdenaAddNetwork;

// Transactions
SignTx: AdenaSignTx;
DoContract: AdenaDoContract;

// Events
On: AdenaOnEvent;
};

export enum EMessageType {
MSG_SEND = '/bank.MsgSend',
MSG_CALL = '/vm.m_call',
MSG_ADD_PKG = '/vm.m_addpkg',
MSG_RUN = '/vm.m_run',
}
11 changes: 11 additions & 0 deletions packages/sdk/src/types/methods/events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export type OnAccountChangeFunc = (address: string) => void;
export type OnNetworkChangeFunc = (network: string) => void;

export enum OnEventType {
CHANGED_ACCOUNT = 'changedAccount',
CHANGED_NETWORK = 'changedNetwork',
}

type OnEventFunc = OnAccountChangeFunc | OnNetworkChangeFunc;

export type AdenaOnEvent = (event: OnEventType, func: OnEventFunc) => void;
38 changes: 38 additions & 0 deletions packages/sdk/src/types/methods/general.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { IAdenaResponse } from '../common';

export enum AddEstablishResponseType {
CONNECTION_SUCCESS = 'CONNECTION_SUCCESS',
ALREADY_CONNECTED = 'ALREADY_CONNECTED',
}

export type AddEstablishResponse = IAdenaResponse<AddEstablishResponseType, Record<string, never>>;

export type AdenaAddEstablish = (name: string) => Promise<AddEstablishResponse>;

enum GetAccountResponseType {
GET_ACCOUNT = 'GET_ACCOUNT',
NO_ACCOUNT = 'NO_ACCOUNT',
WALLET_LOCKED = 'WALLET_LOCKED',
}

enum EAccountStatus {
ACTIVE = 'ACTIVE',
INACTIVE = 'IN_ACTIVE',
}

export type GetAccountResponseData = {
accountNumber: string;
address: string;
coins: string;
chainId: string;
sequence: string;
status: EAccountStatus;
public_key: {
'@type': string;
value: string;
};
};

type GetAccountResponse = IAdenaResponse<GetAccountResponseType, GetAccountResponseData>;

export type AdenaGetAccount = () => Promise<GetAccountResponse>;
29 changes: 29 additions & 0 deletions packages/sdk/src/types/methods/network.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { IAdenaResponse } from '../common';

type AddNetworkParams = {
chainId: string;
chainName: string;
rpcUrl: string;
};

enum AddNetworkResponseType {
ADD_NETWORK_SUCCESS = 'ADD_NETWORK_SUCCESS',
}

type AddNetworkResponse = IAdenaResponse<AddNetworkResponseType, AddNetworkParams>;

export type AdenaAddNetwork = (network: AddNetworkParams) => Promise<AddNetworkResponse>;

export enum SwitchNetworkResponseType {
SWITCH_NETWORK_SUCCESS = 'SWITCH_NETWORK_SUCCESS',
REDUNDANT_CHANGE_REQUEST = 'REDUNDANT_CHANGE_REQUEST',
UNADDED_NETWORK = 'UNADDED_NETWORK',
}

type SwitchNetworkResponseData = {
chainId: string;
};

type SwitchNetworkResponse = IAdenaResponse<SwitchNetworkResponseType, SwitchNetworkResponseData>;

export type AdenaSwitchNetwork = (chainId: string) => Promise<SwitchNetworkResponse>;
Loading

0 comments on commit 155f71d

Please sign in to comment.