본문으로 건너뛰기

Wallet Standard

Sui용으로 구축된 브라우저 확장 지갑은 Wallet Standard를 사용한다. 이것은 dApp이 지갑을 자동으로 발견하고 상호작용할 수 있는 방법을 정의하는 크로스체인 표준이다.

지갑을 구축하는 경우, 헬퍼 라이브러리 @mysten/wallet-standard 는 시작하는 데 도움이 되는 타입과 유틸리티를 제공한다.

Working with wallets

Wallet Standard는 지갑 구축을 돕는 기능을 포함한다.

Creating a wallet interface

지갑을 나타내는 클래스를 생성한다. @mysten/wallet-standardWallet interface를 사용하여 클래스가 표준을 준수하도록 한다.

import { SUI_DEVNET_CHAIN, Wallet } from '@mysten/wallet-standard';

class YourWallet implements Wallet {
get version() {
// 구현하는 Wallet Standard의 version을 반환한다(이 경우 1.0.0).
return '1.0.0';
}

get name() {
return 'Wallet Name';
}

get icon() {
return 'some-icon-data-url';
}

// wallet이 지원하는 Sui chains를 반환한다.
get chains() {
return [SUI_DEVNET_CHAIN];
}
}

Implementing features

기능은 소비자가 지갑과 상호작용하는 데 사용할 수 있는 표준 메서드이다. 지갑은 다음 기능을 구현해야 한다:

Core features

  • standard:connect - 지갑에 account authorization을 요청하는 데 사용한다.
  • standard:events - account가 추가되거나 제거되는 것과 같이 지갑 내에서 발생하는 변경 사항을 수신하는 데 사용한다.

Transaction features

Wallet Standard 위에 구축된 라이브러리(예: dApp Kit)는 transaction 서명 기능이 필요하다. 최대 호환성을 위해 현재 및 레거시 메서드를 모두 구현한다:

Current methods:

  • sui:signTransaction - 사용자에게 transaction에 서명하도록 요청하고 직렬화된 transaction과 서명을 dApp에 반환하는 데 사용한다. 이 메서드는 transaction을 실행을 위해 제출하지 않는다.
  • sui:signAndExecuteTransaction - 사용자에게 transaction에 서명하도록 요청한 다음 blockchain에 실행을 위해 제출하는 데 사용한다.

Legacy methods:

  • sui:signTransactionBlock - sui:signTransaction의 레거시 버전이다. 많은 기존 dApp이 여전히 이 메서드에 의존한다.
  • sui:signAndExecuteTransactionBlock - sui:signAndExecuteTransaction의 레거시 버전이다. 많은 기존 dApp이 여전히 이 메서드에 의존한다.

Personal message signing

  • sui:signPersonalMessage - 사용자에게 개인 메시지에 서명하도록 요청하고 메시지 서명을 dApp에 반환하는 데 사용한다. 이것은 많은 dApp이 사용하는 사용자 검증 흐름에 필수적이다.

지갑 클래스의 features 속성 아래에 이러한 기능을 구현한다:

import {
StandardConnectFeature,
StandardConnectMethod,
StandardEventsFeature,
StandardEventsOnMethod,
SuiFeatures,
SuiSignPersonalMessageMethod,
SuiSignTransactionMethod,
SuiSignAndExecuteTransactionMethod,
StandardConnect,
StandardEvents,
SuiSignPersonalMessage,
SuiSignTransaction,
SuiSignAndExecuteTransaction
} from "@mysten/wallet-standard";

class YourWallet implements Wallet {
/* ... existing code from above ... */

get features(): ConnectFeature & EventsFeature & SuiFeatures {
return {
[StandardConnect]: {
version: "1.0.0",
connect: this.#connect,
},
[StandardEvents]: {
version: "1.0.0",
on: this.#on,
},
[SuiSignPersonalMessage]: {
version: "1.1.0",
signPersonalMessage: this.#signPersonalMessage,
},
[SuiSignTransaction]: {
version: "2.0.0",
signTransaction: this.#signTransaction,
},
[SuiSignAndExecuteTransaction]: {
version: "2.0.0",
signAndExecuteTransaction: this.#signAndExecuteTransaction,
},
};
};

#on: EventsOnMethod = () => {
// wallet의 events on 구현
};

#connect: ConnectMethod = () => {
// wallet의 connect 구현
};

#signPersonalMessage: SuiSignPersonalMessageMethod = () => {
// wallet의 signPersonalMessage 구현
};

#signTransaction: SuiSignTransactionMethod = () => {
// wallet의 signTransaction 구현
};

#signAndExecuteTransaction: SuiSignAndExecuteTransactionMethod = () => {
// wallet의 signAndExecuteTransaction 구현
};
}

Exposing accounts

지갑 interface의 마지막 요구 사항은 accounts interface를 노출하는 것이다. Wallet Standard를 준수하는 지갑은 지갑이 로드될 때 이전에 승인된 account로 이 배열을 자동으로 채워야 한다. 이것은 다음을 의미한다:

  • 첫 방문 시: 사용자가 standard:connect 를 통해 account를 승인할 때까지 배열이 비어 있다.
  • 이후 방문 시: 이전에 승인된 account가 dApp 작업 없이 자동으로 나타난다.
  • 취소 후: 취소된 account는 배열에 나타나지 않는다.

account는 필요한 interface와 일치하는 account를 구성하기 위해 ReadonlyWalletAccount 클래스를 사용한다.

import { ReadonlyWalletAccount } from '@mysten/wallet-standard';

class YourWallet implements Wallet {
get accounts() {
// 이미 account에 대한 내부 표현이 있다고 가정한다:
return someWalletAccounts.map(
(walletAccount) =>
new ReadonlyWalletAccount({
address: walletAccount.suiAddress,
publicKey: walletAccount.pubkey,
// 이 account가 지원하는 Sui chains이다. 이는 wallet이 지원하는 chains의 부분집합일 수 있다.
// 이 chains는 wallet에도 존재해야 한다.
chains: [SUI_DEVNET_CHAIN],
// 이 account가 지원하는 features이다. 이는 wallet이 지원하는 features의 부분집합일 수 있다.
// 이 features는 wallet에도 존재해야 한다.
features: [
SuiSignPersonalMessage,
SuiSignTransaction,
SuiSignAndExecuteTransaction,
],
}),
);
}
}

Registering in the window

지갑에 대한 호환 interface가 있으면 registerWallet 함수를 사용하여 등록한다.

import { registerWallet } from '@mysten/wallet-standard';

registerWallet(new YourWallet());

Managing wallets

Wallet Standard는 앱이 지갑과 상호작용하는 데 도움이 되는 기능을 포함한다.

Wallet data

사용자 브라우저에 설치된 지갑을 쿼리하려면 getWalletsget 함수를 사용한다.

import { getWallets } from '@mysten/wallet-standard';

const availableWallets = getWallets().get();

이 호출의 반환값(이전 코드의 availableWallets)은 Wallet 타입의 배열이다.

웹 페이지에 지갑 세부 정보를 표시하려면 Wallet.iconWallet.name 속성을 사용한다.

Wallet.accountsWalletAccount의 배열이다. 각 WalletAccount 타입은 addresspublicKey 속성을 가지며, 이것은 개발 중에 가장 유용하다. 이 데이터는 account authorization 후 채워지고 캐시된다.

Features

Wallet 타입과 WalletAccount 타입 모두 features라는 속성을 가진다. 주요 지갑 기능은 여기에서 찾을 수 있다. 지갑이 구현해야 하는 필수 기능은 이전 코드에 나열되어 있다.

많은 지갑이 일부 비필수 기능을 생략하거나 일부 사용자 정의 기능을 추가하므로, 특정 지갑을 통합하려는 경우 관련 지갑 문서를 확인한다.

Authorizing wallet accounts

Wallet Standard는 지속적인 authorization 모델을 사용한다. API의 "connect" 용어에도 불구하고, 지갑은 dApp의 작업 없이 로드될 때 이전에 승인된 account를 자동으로 복원해야 한다. connect() 메서드는 필요할 때 사용자에게 account를 승인하도록 요청하는 데 특화되어 있다.

How account authorization works:

  1. 지갑은 account를 자동으로 복원한다: 지갑이 로드될 때 현재 dApp에 대해 이전에 승인된 account로accounts 배열을 자동으로 채워야 한다. 이것은 dApp 상호작용 없이 발생한다.

  2. 필요할 때만 요청한다: 다음 경우에만connect() 를 호출한다:

    • accounts 배열이 비어 있는 경우 (첫 사용자이거나 모든 account가 취소된 경우)
    • 현재 account가 요구 사항을 충족하지 않는 경우 (예: 특정 chain의 account가 필요한 경우)
    • 사용자가 명시적으로 더 많은 account를 승인하려는 경우
await wallet.features['standard:connect'].connect();
정보

connect() 메서드는 사용자 상호작용 없이 이전에 승인된 account를 검색하기 위한 silent 매개변수를 가지지만, 이 매개변수는 Wallet Standard의 향후 버전에서 사용 중단될 예정이다. 지갑은 대신 로드 시 accounts 배열을 자동으로 채워야 한다.

connect()를 호출하면 지갑이 사용자가 dApp이 액세스할 수 있는 account를 선택하고 승인할 수 있도록 팝업을 연다.

Revoking account authorization

connect(account authorization) 기능과 유사하게, Wallet Standard는 dApp의 지갑 account 액세스를 취소하기 위한 standard:disconnect 도 포함한다. 다음 예제는 이 기능을 호출한다:

wallet.features['standard:disconnect'].disconnect();

Transactions - suggested approach

사용자가 지갑에서 account를 승인하면 앱은 address 및 메서드와 같은 transaction을 실행하는 데 필요한 정보를 갖는다.

@mysten/sui 라이브러리로 transaction을 별도로 구성한 다음 사용자의 개인 키로 서명한다.sui:signTransaction 기능을 사용하여 이를 달성한다:

wallet.features['sui:signTransaction'].signTransaction({
transaction: <Transaction>,
account: <WalletAccount>
})

Account authorization과 유사하게, 이 프로세스는 사용자가 transaction을 수락하거나 거부할 수 있는 팝업 대화 상자를 연다. 수락하면 함수는 {bytes: String, signature: Uint8Array}형식의 object를 반환한다. bytes 값은 transaction의 b64 인코딩이고 signature 값은 transaction 서명이다.

Transaction을 실행하려면 @mysten/suiSuiClient를 사용한다:

const client: SuiClient
client.executeTransactionBlock({
transactionBlock: bytes,
signature: signature,
options: {}
})

Transactions - abbreviated approach

많은 지갑이 위의 흐름을 하나의 기능인sui:signAndExecuteTransaction으로 추상화한다. 이 기능에 필요한 인수는 원시 transaction과 응답에 포함할 원하는 정보가 있는 옵션이다:

  • showEffects: Transaction effects를 포함한다.
  • showEvents: Transaction events를 포함한다.
  • showObjectChanges: 삭제, 생성 또는 변경된 모든 object를 포함한다.
  • showBalanceChanges: 발생한 모든 coin 전송을 포함한다.
  • showInput: Transaction의 입력을 포함한다.
  • showRawInput: showInput 과 동일하지만 형식이 원시이다.

Events wallets emit

지갑은 앱이 수신할 수 있는 특정 사용자 작업에서 이벤트를 발생시킨다. 이러한 이벤트를 통해 앱이 지갑의 사용자 작업에 반응할 수 있다.

Wallet Standard는 chain, 기능 또는 account에 적용할 수 있는 변경 이벤트만 정의한다.

  • chains: Chain의 변경 이벤트는 사용자가 Devnet에서 Testnet으로와 같이 지갑의 활성 네트워크를 전환했음을 의미한다.
  • features: 사용자가 앱이 특정 지갑 기능에 액세스할 수 있는 권한을 추가하거나 제거했다.
  • accounts: 사용자가 앱과 상호작용하기 위해 account(address)를 추가하거나 제거했다.

다음 호출로 앱을 이벤트에 구독한다:

const unsubscribe = wallet.features['standard:events'].on('change', callback);

이 호출은 이벤트 수신을 구독 취소하는 데 호출할 수 있는 함수를 반환한다.

콜백은 이벤트가 발생할 때 수행할 로직을 포함하는 핸들러이다. 콜백 함수에 대한 입력은 다음 타입의 객체이다:

{
accounts: WalletAccount[],
chains: IdentifierArray,
features: IdentifierRecord<unknown>
}

이러한 값은 모두 새 항목이나 변경된 항목을 포함하는 배열이다. 결과적으로, 대부분의 경우 모든 이벤트는 하나의 배열만 채우고 나머지는 비어 있다.

Implementation example

Mysten Labs는 @mysten/dapp-kit라는 React 기반 애플리케이션을 위한 기본 스캐폴드를 제공한다. 자세한 내용은 dApp Kit documentation 을 참고한다.