Skip to main content

Advanced usage of Surf Keeper

After you connected a page to the extension you can interact with your Everscale account.

Surf Extension methods

connect

Requests new permissions for current origin. Shows an approval window to the user.

input: {};
output: {
isConnected: boolean; // Flag shows connection status for the current origin
address?: string; // Address of extension wallet
publicKey?: string; // Hex encoded public key
};

Example:

const result = await rpc.connect();

connectStatus

Returns the current connection status.

input: {};
output: {
isConnected: boolean; // Flag shows connection status for the current origin
address?: string; // Address of extension wallet
publicKey?: string; // Hex encoded public key
};

Example:

const result = await rpc.connectStatus();

disconnect

Removes all permissions for current origin.

input: {
}
output: {
isConnected: boolean; // Flag shows connection status for the current origin; should return `false` as disconnect method execution result
}

Example:

const result = await rpc.disconnect();

sendMessage

Sends an internal message from the user account. Shows an approval window to the user.

input: {
abi: string; // Contract abi
address: string; // Address string
net: EverscaleNetNameKey; // Name of network to send message in, i.e. 'mainnet' | 'devnet'
callSet: {
functionName: string; // Name of contract function to be sent to the contract
input: Record<string, any>; // Input for the contract function
header?: FunctionHeader;
};
bounce: boolean; // Whether to bounce message back on error
amount: string; // Amount of nano EVER to send
action?: string; // Name of action to be performed by message send
};
output: {
// Result of send message
result?: {
shard_block_id: string; // The last generated shard block of the message destination account before the message was sent
sending_endpoints: string[]; // The list of endpoints to which the message was sent
};
error?: string; // String with some error details
};

Example:

const response = await rpc.sendMessage({
amount: '2000000000', // in nano-tokens, i.e. 2 tokens
bounce: true,
callSet: {
functionName: 'addComment',
input: {
comment: 'Test comment',
},
},
net: 'mainnet',
action: 'Create comment',
address: '0:8959ea111cc0c85d996df0d16e530d584d5366618cfed9ab6a1754828bb78479',
abi: '{"ABI version":2,"version":"2.3","header":["pubkey","time","expire"]...',
});

sendTransaction

Sends transaction with provided params.

input: {
amount: string; // Amount of nano EVER to send
bounce: boolean; // Whether to bounce message back on error
comment: string; // Comment for the transaction to send it in payload
net: EverscaleNetNameKey; // Name of network to send transaction in, i.e. 'mainnet' | 'devnet'
to: string; // Address to send transaction to
}
output: {
// Result of send transaction message
result?: {
shard_block_id: string; // The last generated shard block of the message destination account before the message was sent
sending_endpoints: string[]; // The list of endpoints to which the message was sent
};
error?: string; // String with some error details
};

Example:

const response = await rpc.sendTransaction({
amount: '10000000000', // in nano-tokens, i.e. 10 tokens
bounce: true,
comment: 'check it out!',
net: 'devnet',
to: '0:b76b532fbe72307bff243b401d6792d5d01332ea294a0310c0ffdf874026f2b9',
});

signData

Signs arbitrary data.

input: {
data: string; // Base64 encoded arbitrary bytes
}
output: {
signature?: string; // Base64 encoded signature bytes (data is guaranteed to be 64 bytes long)
error?: string; // String with error details
};

Example:

const response = await rpc.signData({
data: 'te6ccgEBAQEAKAAASw4E0p6AD5fz9JsGWfbBhP0Bwq9+jk0X3za9rhuI7A1H3DxC0QBw',
});

subscribe

Subscribes to data updates.

input: {
type: string; // Subscription type, for now only "balance" is available
address: string; // Target address
listener: (value: string) => void; // Subscription data update handler
};
output: {
remove: () => void; // Base64 encoded signature bytes (data is guaranteed to be 64 bytes long)
};

Example:

const response = rpc.subscribe({
type: 'balance',
address: '0x000000..000',
listener: val => console.log('Balance uodated: ', val),
});

Frameworks usage

Here we gathered the most common patterns of @eversurf/surfkeeper-provider usage:

import { useEffect, useMemo, useState } from 'react';
import {
Address,
ConnectResponse,
ProviderRpcClient,
hasSurfKeeperProvider
} from '@eversurf/surfkeeper-provider';

export type ExtensionState = {
hasProvider: boolean;
isConnecting: boolean;
isConnected: boolean;
isContractUpdating: boolean;
isDisconnecting: boolean;
isInitialized: boolean;
isInitializing: boolean;
}
export type ExtensionData = {
address: Address | undefined;
publicKey: string | undefined;
}
const DEFAULT_EXTENSION_STATE: ExtensionState = {
hasProvider: false,
isConnecting: false,
isConnected: false,
isContractUpdating: false,
isDisconnecting: false,
isInitialized: false,
isInitializing: false,
}
const DEFAULT_EXTENSION_DATA: ExtensionData = {
address: undefined,
publicKey: undefined
}

export interface IUseSurfKeeper {
rpc: ProviderRpcClient,
connect: () => Promise<void>,
extensionState: ExtensionState,
extensionData: ExtensionData
}

export const rpc = new ProviderRpcClient();

export const useSurfKeeper = (): IUseSurfKeeper => {
const [extensionState, setExtensionState] = useState<ExtensionState>(DEFAULT_EXTENSION_STATE);
const [extensionData, setExtensionData] = useState<ExtensionData>(DEFAULT_EXTENSION_DATA);

const init = async () => {
setExtensionState({
...extensionState,
isInitializing: true
});

let hasProvider = false;

try {
hasProvider = await hasSurfKeeperProvider();
}
catch (e) {
console.log("hasSurfKeeperProvider error");
}

if (!hasProvider) {
setExtensionState({
...extensionState,
hasProvider: false,
isInitializing: false
});
console.warn("Surf Keeper is not installed");
}

const connectResponse = await rpc.connectStatus();

if (connectResponse!.isConnected) {
setExtensionState({
...extensionState,
isConnected: true,
hasProvider: hasProvider,
isConnecting: false,
isInitialized: true,
isInitializing: false,
});
setExtensionData({
address: (connectResponse as unknown as ConnectResponse).address,
publicKey: (connectResponse as unknown as ConnectResponse).publicKey
});
} else {
setExtensionState({
...extensionState,
isConnected: false,
hasProvider: hasProvider,
isConnecting: false,
isInitialized: true,
isInitializing: false,
});
console.warn("Surf Keeper is not connected");
}
}

const connect = async () => {
const connectResponse = await rpc.connect();

if (connectResponse!.isConnected) {
setExtensionState({
...extensionState,
isConnecting: false,
isConnected: true,
isInitialized: true,
isInitializing: false,
});
setExtensionData({
address: (connectResponse as unknown as ConnectResponse).address,
publicKey: (connectResponse as unknown as ConnectResponse).publicKey
});
return Promise.resolve();
} else {
return Promise.reject();
}
};

useEffect(() => {
setExtensionState({
...extensionState,
isInitializing: true
});
init();
}, []);

return useMemo(
() => ({
rpc: rpc,
connect: connect,
extensionState: extensionState,
extensionData: extensionData
}),
[extensionState, extensionData],
);
};

export default useSurfKeeper;