Skip to content

sui_derivable_account - [devnet]

Derivable account abstraction that verifies a message signed by Sui wallet.

  1. The message format is as follows:

wants you to sign in with your Sui account: <sui_account_address>

Please confirm you explicitly initiated this request from . You are approving to execute transaction <entry_function_name> on Aptos blockchain (<network_name>).

Nonce:

  1. The abstract public key is a BCS serialized SuiAbstractPublicKey.
  2. The abstract signature is a BCS serialized SuiAbstractSignature.
  3. This module has been tested for the following wallets:
use 0x1::aptos_hash;
use 0x1::auth_data;
use 0x1::bcs;
use 0x1::bcs_stream;
use 0x1::common_account_abstractions_utils;
use 0x1::ed25519;
use 0x1::option;
use 0x1::string;
use 0x1::string_utils;
use 0x1::transaction_context;
use 0x1::vector;

Enum SuiAbstractSignature

enum SuiAbstractSignature has drop
Variants
MessageV1
Fields
signature: vector<u8>
The signature of the message in raw bytes

Constants

Invalid public key.

const EINVALID_PUBLIC_KEY: u64 = 6;

Entry function payload is missing.

const EMISSING_ENTRY_FUNCTION_PAYLOAD: u64 = 1;

Invalid signature.

const EINVALID_SIGNATURE: u64 = 5;

Invalid signature type.

const EINVALID_SIGNATURE_TYPE: u64 = 2;

Account address mismatch.

const EACCOUNT_ADDRESS_MISMATCH: u64 = 7;

Invalid signature length.

const EINVALID_SIGNATURE_LENGTH: u64 = 4;

Invalid signing scheme type.

const EINVALID_SIGNING_SCHEME_TYPE: u64 = 3;

Structs

SuiAbstractPublicKey

Sui abstract public key defined with the

struct SuiAbstractPublicKey has drop
Fields
sui_account_address: vector<u8>
domain: vector<u8>

IntentMessage

A wrapper struct that defines a message with its signing context (intent). https://github.com/MystenLabs/sui/blob/main/crates/shared-crypto/src/intent.rs#L168

struct IntentMessage has copy, drop, store
Fields
intent: sui_derivable_account::Intent
value: vector<u8>

Intent

Metadata specifying the scope, version, and application domain of the message. https://github.com/MystenLabs/sui/blob/main/crates/shared-crypto/src/intent.rs#L86

struct Intent has copy, drop, store
Fields
scope: sui_derivable_account::IntentScope
version: sui_derivable_account::IntentVersion
app_id: sui_derivable_account::AppId

Functions

get_signing_scheme

Returns the signing scheme for the given value.

fun get_signing_scheme(value: u8): sui_derivable_account::SuiSigningScheme
Implementation
fun get_signing_scheme(value: u8): SuiSigningScheme {
if (value == 0) SuiSigningScheme::ED25519
else abort(EINVALID_SIGNING_SCHEME_TYPE)
}

deserialize_abstract_public_key

Deserializes the abstract public key which is supposed to be a bcs serialized SuiAbstractPublicKey.

fun deserialize_abstract_public_key(abstract_public_key: &vector<u8>): sui_derivable_account::SuiAbstractPublicKey
Implementation
fun deserialize_abstract_public_key(abstract_public_key: &vector<u8>): SuiAbstractPublicKey {
let stream = bcs_stream::new(*abstract_public_key);
let sui_account_address = bcs_stream::deserialize_vector<u8>(&mut stream, |x| deserialize_u8(x));
let domain = bcs_stream::deserialize_vector<u8>(&mut stream, |x| deserialize_u8(x));
SuiAbstractPublicKey { sui_account_address, domain }
}

deserialize_abstract_signature

Returns a tuple of the signature.

fun deserialize_abstract_signature(abstract_signature: &vector<u8>): sui_derivable_account::SuiAbstractSignature
Implementation
fun deserialize_abstract_signature(abstract_signature: &vector<u8>): SuiAbstractSignature {
let stream = bcs_stream::new(*abstract_signature);
let signature_type = bcs_stream::deserialize_u8(&mut stream);
if (signature_type == 0x00) {
let signature = bcs_stream::deserialize_vector<u8>(&mut stream, |x| deserialize_u8(x));
SuiAbstractSignature::MessageV1 { signature }
} else {
abort(EINVALID_SIGNATURE_TYPE)
}
}

split_signature_bytes

Splits raw signature bytes containing scheme flag (1 byte), signature (64 bytes) and public key (32 bytes) to a tuple of (signing_scheme, signature, public_key)

public fun split_signature_bytes(bytes: &vector<u8>): (u8, vector<u8>, vector<u8>)
Implementation
public fun split_signature_bytes(bytes: &vector<u8>): (u8, vector<u8>, vector<u8>) {
// 1 + 64 + 32 = 97 bytes
assert!(bytes.length() == 97, EINVALID_SIGNATURE_LENGTH);
let signing_scheme = bytes[0];
let abstract_signature_signature = vector::empty<u8>();
let abstract_signature_public_key = vector::empty<u8>();
// Extract signature (64 bytes)
let i = 1;
while (i < 65) {
abstract_signature_signature.push_back(bytes[i]);
i += 1;
};
// Extract public key (32 bytes)
while (i < 97) {
abstract_signature_public_key.push_back(bytes[i]);
i += 1;
};
(signing_scheme, abstract_signature_signature, abstract_signature_public_key)
}

derive_account_address_from_public_key

Derives the account address from the public key and returns it is a hex string with “0x” prefix

fun derive_account_address_from_public_key(signing_scheme: u8, public_key_bytes: vector<u8>): vector<u8>
Implementation
fun derive_account_address_from_public_key(signing_scheme: u8, public_key_bytes: vector<u8>): vector<u8> {
// Create a vector with signing scheme and public key bytes
let data_to_hash = vector[signing_scheme];
data_to_hash.append(public_key_bytes);
// Compute blake2b hash
let sui_account_address = aptos_hash::blake2b_256(data_to_hash);
// Convert the address bytes to a hex string with "0x" prefix
let sui_account_address_hex = b"0x";
let i = 0;
while (i < sui_account_address.length()) {
let byte = sui_account_address[i];
// Convert each byte to two hex characters
let hex_chars = vector[
if ((byte >> 4) < 10) ((byte >> 4) + 0x30) else ((byte >> 4) - 10 + 0x61),
if ((byte & 0xf) < 10) ((byte & 0xf) + 0x30) else ((byte & 0xf) - 10 + 0x61)
];
sui_account_address_hex.append(hex_chars);
i += 1;
};
// Return the account address as hex string
sui_account_address_hex
}

authenticate_auth_data

public fun authenticate_auth_data(aa_auth_data: auth_data::AbstractionAuthData, entry_function_name: &vector<u8>)
Implementation
public fun authenticate_auth_data(
aa_auth_data: AbstractionAuthData,
entry_function_name: &vector<u8>
) {
let abstract_signature = deserialize_abstract_signature(aa_auth_data.derivable_abstract_signature());
let (signing_scheme, abstract_signature_signature, abstract_signature_public_key) = split_signature_bytes(&abstract_signature.signature);
// Check siging scheme is Ed25519 as we currently only support this scheme
assert!(get_signing_scheme(signing_scheme) == SuiSigningScheme::ED25519, EINVALID_SIGNING_SCHEME_TYPE);
// Derive the account address from the public key
let sui_account_address = derive_account_address_from_public_key(signing_scheme, abstract_signature_public_key);
let derivable_abstract_public_key = aa_auth_data.derivable_abstract_public_key();
let abstract_public_key = deserialize_abstract_public_key(derivable_abstract_public_key);
// Check the account address matches the abstract public key
assert!(&sui_account_address == &abstract_public_key.sui_account_address, EACCOUNT_ADDRESS_MISMATCH);
let public_key = new_validated_public_key_from_bytes(abstract_signature_public_key);
assert!(public_key.is_some(), EINVALID_PUBLIC_KEY);
let digest_utf8 = string_utils::to_string(aa_auth_data.digest()).bytes();
// Build the raw message
let raw_message = construct_message(&b"Sui", &sui_account_address, &abstract_public_key.domain, entry_function_name, digest_utf8);
// Prepend Intent to the message
let intent = Intent {
scope: PersonalMessage,
version: V0,
app_id: Sui,
};
let msg = IntentMessage {
intent,
value: raw_message,
};
// Serialize the whole struct
let bcs_bytes = bcs::to_bytes<IntentMessage>(&msg);
// Hash full_message with blake2b256
let hash = aptos_hash::blake2b_256(bcs_bytes);
let signature = new_signature_from_bytes(abstract_signature_signature);
assert!(
ed25519::signature_verify_strict(
&signature,
&public_key_into_unvalidated(public_key.destroy_some()),
hash,
),
EINVALID_SIGNATURE
);
}

authenticate

Authorization function for domain account abstraction.

public fun authenticate(account: signer, aa_auth_data: auth_data::AbstractionAuthData): signer
Implementation
public fun authenticate(account: signer, aa_auth_data: AbstractionAuthData): signer {
daa_authenticate(account, aa_auth_data, |auth_data, entry_name| authenticate_auth_data(auth_data, entry_name))
}

Specification

derive_account_address_from_public_key

fun derive_account_address_from_public_key(signing_scheme: u8, public_key_bytes: vector<u8>): vector<u8>
pragma verify = false;

authenticate_auth_data

public fun authenticate_auth_data(aa_auth_data: auth_data::AbstractionAuthData, entry_function_name: &vector<u8>)
pragma verify = false;

authenticate

public fun authenticate(account: signer, aa_auth_data: auth_data::AbstractionAuthData): signer
pragma verify = false;

Enum SuiSigningScheme

Sui signing scheme as defined in https://github.com/MystenLabs/ts-sdks/blob/main/packages/typescript/src/cryptography/signature-scheme.ts#L19

enum SuiSigningScheme has drop
Variants
ED25519
Fields

Enum IntentScope

https://github.com/MystenLabs/sui/blob/main/crates/shared-crypto/src/intent.rs#L60

enum IntentScope has copy, drop, store
Variants
TransactionData
Fields
TransactionEffects
Fields
CheckpointSummary
Fields
PersonalMessage
Fields

Enum IntentVersion

https://github.com/MystenLabs/sui/blob/main/crates/shared-crypto/src/intent.rs#L18

enum IntentVersion has copy, drop, store
Variants
V0
Fields

Enum AppId

https://github.com/MystenLabs/sui/blob/main/crates/shared-crypto/src/intent.rs#L35

enum AppId has copy, drop, store
Variants
Sui
Fields