Once you’ve constructed a PINT payload, it needs to be signed before it can be exchanged for a JWT. The Sumvin ecosystem supports two signer types, each using different cryptographic curves and verification methods.
Signer Types
| Signer Type | Key Curve | Verification Method | Use Case |
|---|
user | secp256k1 | Standard ECDSA recovery | User signing directly via wallet |
agent | P-256 (secp256r1) | EIP-1271 isValidSignature against Safe contract | AI agent signing on user’s behalf |
The SIS automatically determines the signer type based on whether the signing wallet has a registered agent key.
User Signing (secp256k1)
When the user signs directly, use standard Ethereum EIP-712 signing via their wallet. This is the most common path for user-initiated flows.
TypeScript (viem)
import { createWalletClient, custom } from "viem";
import { sei } from "viem/chains";
const walletClient = createWalletClient({
chain: sei,
transport: custom(window.ethereum),
});
const domain = {
name: "Sumvin Purchase Intent",
version: "1",
chainId: 1329n,
verifyingContract: "0xE23c9A70BC749EBddd8c78a864fd911D04E9e992",
} as const;
const types = {
PurchaseIntent: [
{ name: "wallet", type: "address" },
{ name: "nonce", type: "uint256" },
{ name: "statement", type: "string" },
{ name: "scopes", type: "string[]" },
{ name: "resources", type: "string[]" },
{ name: "maxAmount", type: "uint256" },
{ name: "maxAmountToken", type: "address" },
{ name: "expiresAt", type: "uint256" },
],
} as const;
const message = {
wallet: "0xE23c9A70BC749EBddd8c78a864fd911D04E9e992",
nonce: 42n,
statement: "Purchase authorization for partner X",
scopes: ["sr:us:pint:identity:proof_of_personhood", "sr:us:pint:personalization:read"],
resources: ["sr:us:pint:abc123"],
maxAmount: 0n,
maxAmountToken: "0x0000000000000000000000000000000000000000",
expiresAt: 1740000000n,
};
const signature = await walletClient.signTypedData({
account: "0xUserEOAAddress...",
domain,
types,
primaryType: "PurchaseIntent",
message,
});
// signature is a hex string: "0x..."
Python (eth-account)
from eth_account import Account
from eth_account.messages import encode_typed_data
domain_data = {
"name": "Sumvin Purchase Intent",
"version": "1",
"chainId": 1329,
"verifyingContract": "0xE23c9A70BC749EBddd8c78a864fd911D04E9e992",
}
message_types = {
"PurchaseIntent": [
{"name": "wallet", "type": "address"},
{"name": "nonce", "type": "uint256"},
{"name": "statement", "type": "string"},
{"name": "scopes", "type": "string[]"},
{"name": "resources", "type": "string[]"},
{"name": "maxAmount", "type": "uint256"},
{"name": "maxAmountToken", "type": "address"},
{"name": "expiresAt", "type": "uint256"},
]
}
message_data = {
"wallet": "0xE23c9A70BC749EBddd8c78a864fd911D04E9e992",
"nonce": 42,
"statement": "Purchase authorization for partner X",
"scopes": ["sr:us:pint:identity:proof_of_personhood", "sr:us:pint:personalization:read"],
"resources": ["sr:us:pint:abc123"],
"maxAmount": 0,
"maxAmountToken": "0x0000000000000000000000000000000000000000",
"expiresAt": 1740000000,
}
signable = encode_typed_data(
domain_data, message_types, message_data, "PurchaseIntent"
)
signed = Account.sign_message(signable, private_key="0xYourPrivateKey...")
signature = signed.signature.hex()
Agent Signing (P-256)
When an AI agent signs on the user’s behalf, it uses a P-256 key that has been registered as an authorised signer on the user’s Safe smart account. Verification happens on-chain via EIP-1271 — the SIS calls isValidSignature on the Safe contract to confirm the agent key is authorised.
import { p256 } from "@noble/curves/p256";
// Agent's P-256 private key (provisioned during agent setup)
const agentPrivateKey = new Uint8Array(/* ... */);
// Hash the EIP-712 typed data (same hash as user signing)
const typedDataHash = hashTypedData({ domain, types, primaryType: "PurchaseIntent", message });
// Sign with P-256
const sig = p256.sign(typedDataHash.slice(2), agentPrivateKey);
const signature = "0x" + sig.toDERHex();
When exchanging an agent-signed PINT, the SIS detects the agent key automatically — no need to specify signer_type in the request:
{
"pint": { "...": "..." },
"signature": "0x...",
"audience": "partner-x.example.com"
}
Agent signing requires the P-256 key to be registered on the user’s Safe contract. If the key is not an authorised signer, the EIP-1271 verification will fail with error code PINT-401-002.
After Signing
Once you have the PINT payload and signature, pass both to the token exchange endpoint to receive a SIS-signed JWT.