This guide covers account creation, permission management, and multisig operations on XPR Network.
| Rule | Description |
|---|---|
| Length | 1-12 characters |
| Characters | Lowercase a-z and digits 1-5 only |
| No dots | Unlike EOS, XPR accounts cannot contain dots |
| No uppercase | Must be entirely lowercase |
Valid: alice, mycontract, game123, x
Invalid: Alice, my.contract, game_123, user@home
Every account has:
- owner permission - Highest authority, can change all permissions
- active permission - Standard transaction signing
- Custom permissions (optional) - Limited access for specific actions
Account: mycontract
├── owner (threshold: 1)
│ └── PUB_K1_owner_key (weight: 1)
└── active (threshold: 1)
├── PUB_K1_active_key (weight: 1)
└── mycontract@eosio.code (weight: 1) ← For inline actions
# Create new account (requires existing account to pay)
proton account:create newaccount
# Create with specific key
proton account:create newaccount --key PUB_K1_xxxxx
# Create on testnet
proton chain:set proton-test
proton account:create testaccount// Create account programmatically
const actions = [{
account: 'eosio',
name: 'newaccount',
authorization: [{ actor: creator, permission: 'active' }],
data: {
creator: creator,
name: 'newaccount',
owner: {
threshold: 1,
keys: [{ key: 'PUB_K1_xxxxx', weight: 1 }],
accounts: [],
waits: []
},
active: {
threshold: 1,
keys: [{ key: 'PUB_K1_xxxxx', weight: 1 }],
accounts: [],
waits: []
}
}
},
{
account: 'eosio',
name: 'buyrambytes',
authorization: [{ actor: creator, permission: 'active' }],
data: {
payer: creator,
receiver: 'newaccount',
bytes: 8192 // Initial RAM
}
}];
await session.transact({ actions }, { broadcast: true });On mainnet, accounts can be created free through:
- WebAuth wallet (automatic)
- Proton Resources portal
On testnet:
proton faucet:claim XPR newaccountproton account myaccountOutput shows:
permissions:
owner 1: 1 PUB_K1_xxxxx
active 1: 1 PUB_K1_yyyyy
proton action eosio updateauth '{
"account": "myaccount",
"permission": "active",
"parent": "owner",
"auth": {
"threshold": 1,
"keys": [
{"key": "PUB_K1_newkey", "weight": 1}
],
"accounts": [],
"waits": []
}
}' myaccount@ownerFor a contract to send inline actions, it needs eosio.code permission:
proton action eosio updateauth '{
"account": "mycontract",
"permission": "active",
"parent": "owner",
"auth": {
"threshold": 1,
"keys": [
{"key": "PUB_K1_contractkey", "weight": 1}
],
"accounts": [
{"permission": {"actor": "mycontract", "permission": "eosio.code"}, "weight": 1}
],
"waits": []
}
}' mycontract@owner# Create 'minter' permission under 'active'
proton action eosio updateauth '{
"account": "myaccount",
"permission": "minter",
"parent": "active",
"auth": {
"threshold": 1,
"keys": [
{"key": "PUB_K1_minterkey", "weight": 1}
],
"accounts": [],
"waits": []
}
}' myaccount@owner
# Link permission to specific action
proton action eosio linkauth '{
"account": "myaccount",
"code": "atomicassets",
"type": "mintasset",
"requirement": "minter"
}' myaccount@ownerNow signing with the minter key only allows calling atomicassets::mintasset.
proton action eosio deleteauth '{
"account": "myaccount",
"permission": "minter"
}' myaccount@ownerproton action eosio unlinkauth '{
"account": "myaccount",
"code": "atomicassets",
"type": "mintasset"
}' myaccount@ownerMultisig requires multiple parties to approve a transaction.
2-of-3 multisig:
proton action eosio updateauth '{
"account": "multisig",
"permission": "active",
"parent": "owner",
"auth": {
"threshold": 2,
"keys": [],
"accounts": [
{"permission": {"actor": "alice", "permission": "active"}, "weight": 1},
{"permission": {"actor": "bob", "permission": "active"}, "weight": 1},
{"permission": {"actor": "charlie", "permission": "active"}, "weight": 1}
],
"waits": []
}
}' multisig@owner# Create proposal
proton msig:propose myproposal '{
"actions": [{
"account": "eosio.token",
"name": "transfer",
"authorization": [{"actor": "multisig", "permission": "active"}],
"data": {
"from": "multisig",
"to": "recipient",
"quantity": "100.0000 XPR",
"memo": "Approved payment"
}
}]
}' proposer# Alice approves
proton msig:approve proposer myproposal alice
# Bob approves
proton msig:approve proposer myproposal bobOnce threshold is met:
proton msig:exec proposer myproposal executorproton msig:cancel myproposal proposer// Propose
const proposeActions = [{
account: 'eosio.msig',
name: 'propose',
authorization: [{ actor: proposer, permission: 'active' }],
data: {
proposer: proposer,
proposal_name: 'myprop',
requested: [
{ actor: 'alice', permission: 'active' },
{ actor: 'bob', permission: 'active' }
],
trx: {
expiration: '2025-12-31T23:59:59',
ref_block_num: 0,
ref_block_prefix: 0,
max_net_usage_words: 0,
max_cpu_usage_ms: 0,
delay_sec: 0,
context_free_actions: [],
actions: [/* the proposed actions */],
transaction_extensions: []
}
}
}];
// Approve
const approveActions = [{
account: 'eosio.msig',
name: 'approve',
authorization: [{ actor: approver, permission: 'active' }],
data: {
proposer: proposer,
proposal_name: 'myprop',
level: { actor: approver, permission: 'active' }
}
}];
// Execute
const execActions = [{
account: 'eosio.msig',
name: 'exec',
authorization: [{ actor: executor, permission: 'active' }],
data: {
proposer: proposer,
proposal_name: 'myprop',
executer: executor
}
}];Add a time delay before permission can be used:
proton action eosio updateauth '{
"account": "myaccount",
"permission": "delayed",
"parent": "active",
"auth": {
"threshold": 1,
"keys": [
{"key": "PUB_K1_delayedkey", "weight": 1}
],
"accounts": [],
"waits": [
{"wait_sec": 86400, "weight": 1}
]
}
}' myaccount@ownerThis requires a 24-hour delay before the key can authorize transactions.
Standard secp256k1 keys:
Private: PVT_K1_xxxxx
Public: PUB_K1_xxxxx
Generated with:
proton key:generateHardware-backed keys (Face ID, fingerprint, security keys):
Public: PUB_WA_xxxxx
Cannot be used from CLI - only through WebAuth wallet.
secp256r1 keys (less common):
Private: PVT_R1_xxxxx
Public: PUB_R1_xxxxx
// In contract
@table("config", singleton)
class Config extends Table {
constructor(
public owner: Name = new Name(),
public admins: Name[] = []
) { super(); }
}
function isAdmin(account: Name): boolean {
const config = this.configSingleton.get();
if (account == config.owner) return true;
return config.admins.includes(account);
}
@action("adminaction")
adminAction(admin: Name): void {
requireAuth(admin);
check(this.isAdmin(admin), "Not an admin");
// ... admin logic
}
@action("addadmin")
addAdmin(newAdmin: Name): void {
const config = this.configSingleton.get();
requireAuth(config.owner); // Only owner can add admins
if (!config.admins.includes(newAdmin)) {
config.admins.push(newAdmin);
this.configSingleton.set(config, this.receiver);
}
}Account: myapp
├── owner (cold storage, rarely used)
├── active (standard operations)
├── admin (config changes)
│ └── Linked to: myapp::setconfig, myapp::pause
├── operator (daily operations)
│ └── Linked to: myapp::process, myapp::update
└── minter (limited NFT minting)
└── Linked to: atomicassets::mintasset
# Owner can always recover by updating active
proton action eosio updateauth '{
"account": "myaccount",
"permission": "active",
"parent": "owner",
"auth": {
"threshold": 1,
"keys": [{"key": "PUB_K1_newkey", "weight": 1}],
"accounts": [],
"waits": []
}
}' myaccount@owner| Key | Use | Storage |
|---|---|---|
| Owner | Emergency recovery only | Cold storage (hardware wallet) |
| Active | Standard operations | Hot wallet with strong security |
| Custom | Limited operations | Can be on server for automation |
The owner key should be:
- Generated offline
- Stored in multiple secure locations
- Never used for routine operations
- Used only to recover active key if compromised
Instead of giving a key full active permission, link it to specific actions:
# Create limited permission
proton action eosio updateauth '{
"account": "mybot",
"permission": "resolver",
"parent": "active",
"auth": {"threshold":1,"keys":[{"key":"PUB_K1_botkey","weight":1}],"accounts":[],"waits":[]}
}' mybot@owner
# Only allow calling resolve action
proton action eosio linkauth '{
"account": "mybot",
"code": "pricebattle",
"type": "resolve",
"requirement": "resolver"
}' mybot@ownerIf the bot key is compromised, attacker can only call resolve, not transfer funds.
# Generate new key
proton key:generate
# Update active permission
proton action eosio updateauth '{
"account": "myaccount",
"permission": "active",
"parent": "owner",
"auth": {"threshold":1,"keys":[{"key":"PUB_K1_newkey","weight":1}],"accounts":[],"waits":[]}
}' myaccount@owner
# Remove old key from storage
proton key:remove PVT_K1_oldkey| Command | Description |
|---|---|
proton account NAME |
View account info |
proton account:create NAME |
Create new account |
proton key:generate |
Generate new key pair |
proton key:add |
Add key to local storage |
proton key:list |
List stored keys |
proton msig:propose |
Create multisig proposal |
proton msig:approve |
Approve proposal |
proton msig:exec |
Execute approved proposal |
owner (highest)
└── active (standard)
└── custom permissions (limited)
Child permissions cannot exceed parent's authority.