@openzeppelin/adapter-stellar 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +272 -0
- package/dist/config.cjs +21 -0
- package/dist/config.cjs.map +1 -0
- package/dist/config.d.cts +8 -0
- package/dist/config.d.cts.map +1 -0
- package/dist/config.d.mts +8 -0
- package/dist/config.d.mts.map +1 -0
- package/dist/config.mjs +20 -0
- package/dist/config.mjs.map +1 -0
- package/dist/index.cjs +7564 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +261 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +263 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +7529 -0
- package/dist/index.mjs.map +1 -0
- package/dist/metadata.cjs +22 -0
- package/dist/metadata.cjs.map +1 -0
- package/dist/metadata.d.cts +7 -0
- package/dist/metadata.d.cts.map +1 -0
- package/dist/metadata.d.mts +7 -0
- package/dist/metadata.d.mts.map +1 -0
- package/dist/metadata.mjs +21 -0
- package/dist/metadata.mjs.map +1 -0
- package/dist/networks-BrV516-R.d.cts +15 -0
- package/dist/networks-BrV516-R.d.cts.map +1 -0
- package/dist/networks-C0MmhJcu.d.mts +15 -0
- package/dist/networks-C0MmhJcu.d.mts.map +1 -0
- package/dist/networks-DgUFSTiC.cjs +76 -0
- package/dist/networks-DgUFSTiC.cjs.map +1 -0
- package/dist/networks-QbEPbaGT.mjs +46 -0
- package/dist/networks-QbEPbaGT.mjs.map +1 -0
- package/dist/networks.cjs +8 -0
- package/dist/networks.d.cts +2 -0
- package/dist/networks.d.mts +2 -0
- package/dist/networks.mjs +3 -0
- package/dist/vite-config.cjs +43 -0
- package/dist/vite-config.cjs.map +1 -0
- package/dist/vite-config.d.cts +35 -0
- package/dist/vite-config.d.cts.map +1 -0
- package/dist/vite-config.d.mts +35 -0
- package/dist/vite-config.d.mts.map +1 -0
- package/dist/vite-config.mjs +42 -0
- package/dist/vite-config.mjs.map +1 -0
- package/package.json +114 -0
- package/src/__tests__/getDefaultServiceConfig.test.ts +105 -0
- package/src/access-control/actions.ts +214 -0
- package/src/access-control/feature-detection.ts +238 -0
- package/src/access-control/index.ts +54 -0
- package/src/access-control/indexer-client.ts +1474 -0
- package/src/access-control/onchain-reader.ts +446 -0
- package/src/access-control/service.ts +1431 -0
- package/src/access-control/validation.ts +256 -0
- package/src/adapter.ts +659 -0
- package/src/config.ts +43 -0
- package/src/configuration/__tests__/explorer.test.ts +80 -0
- package/src/configuration/__tests__/rpc.test.ts +355 -0
- package/src/configuration/execution.ts +83 -0
- package/src/configuration/explorer.ts +105 -0
- package/src/configuration/index.ts +5 -0
- package/src/configuration/network-services.ts +210 -0
- package/src/configuration/rpc.ts +270 -0
- package/src/configuration.ts +2 -0
- package/src/contract/__tests__/complete-type-coverage.test.ts +78 -0
- package/src/contract/index.ts +3 -0
- package/src/contract/loader.ts +498 -0
- package/src/contract/transformer.ts +1 -0
- package/src/contract/type.ts +65 -0
- package/src/index.ts +23 -0
- package/src/mapping/constants.ts +89 -0
- package/src/mapping/enum-metadata.ts +237 -0
- package/src/mapping/field-generator.ts +296 -0
- package/src/mapping/index.ts +5 -0
- package/src/mapping/struct-fields.ts +106 -0
- package/src/mapping/tuple-components.ts +43 -0
- package/src/mapping/type-coverage-validator.ts +151 -0
- package/src/mapping/type-mapper.ts +203 -0
- package/src/metadata.ts +16 -0
- package/src/networks/README.md +84 -0
- package/src/networks/index.ts +19 -0
- package/src/networks/mainnet.ts +20 -0
- package/src/networks/testnet.ts +20 -0
- package/src/networks.ts +2 -0
- package/src/query/handler.ts +411 -0
- package/src/query/index.ts +4 -0
- package/src/query/view-checker.ts +32 -0
- package/src/sac/spec-cache.ts +68 -0
- package/src/sac/spec-source.ts +35 -0
- package/src/sac/xdr.ts +101 -0
- package/src/transaction/components/AdvancedInfo.tsx +34 -0
- package/src/transaction/components/FeeConfiguration.tsx +41 -0
- package/src/transaction/components/StellarRelayerOptions.tsx +60 -0
- package/src/transaction/components/TransactionTiming.tsx +77 -0
- package/src/transaction/components/index.ts +5 -0
- package/src/transaction/components/useStellarRelayerOptions.ts +114 -0
- package/src/transaction/eoa.ts +229 -0
- package/src/transaction/execution-strategy.ts +33 -0
- package/src/transaction/formatter.ts +296 -0
- package/src/transaction/index.ts +4 -0
- package/src/transaction/relayer.ts +575 -0
- package/src/transaction/sender.ts +156 -0
- package/src/transform/index.ts +4 -0
- package/src/transform/input-parser.ts +9 -0
- package/src/transform/output-formatter.ts +133 -0
- package/src/transform/parsers/complex-parser.ts +157 -0
- package/src/transform/parsers/generic-parser.ts +171 -0
- package/src/transform/parsers/index.ts +86 -0
- package/src/transform/parsers/primitive-parser.ts +123 -0
- package/src/transform/parsers/scval-converter.ts +405 -0
- package/src/transform/parsers/struct-parser.ts +324 -0
- package/src/transform/parsers/types.ts +35 -0
- package/src/types/__tests__/artifacts.test.ts +89 -0
- package/src/types/artifacts.ts +19 -0
- package/src/utils/__tests__/artifacts.test.ts +77 -0
- package/src/utils/artifacts.ts +30 -0
- package/src/utils/formatting.ts +122 -0
- package/src/utils/index.ts +6 -0
- package/src/utils/input-parsing.ts +336 -0
- package/src/utils/safe-type-parser.ts +303 -0
- package/src/utils/stellar-types.ts +35 -0
- package/src/utils/type-detection.ts +163 -0
- package/src/utils/xdr-ordering.ts +36 -0
- package/src/validation/__tests__/address.test.ts +267 -0
- package/src/validation/address.ts +136 -0
- package/src/validation/eoa.ts +33 -0
- package/src/validation/index.ts +3 -0
- package/src/validation/relayer.ts +13 -0
- package/src/vite-config.ts +67 -0
- package/src/wallet/README.md +93 -0
- package/src/wallet/__tests__/connection.test.ts +72 -0
- package/src/wallet/components/StellarWalletUiRoot.tsx +161 -0
- package/src/wallet/components/account/AccountDisplay.tsx +50 -0
- package/src/wallet/components/connect/ConnectButton.tsx +100 -0
- package/src/wallet/components/connect/ConnectorDialog.tsx +125 -0
- package/src/wallet/components/index.ts +3 -0
- package/src/wallet/connection.ts +151 -0
- package/src/wallet/context/StellarWalletContext.ts +32 -0
- package/src/wallet/context/index.ts +4 -0
- package/src/wallet/context/useStellarWalletContext.ts +17 -0
- package/src/wallet/hooks/facade-hooks.ts +31 -0
- package/src/wallet/hooks/index.ts +7 -0
- package/src/wallet/hooks/useStellarAccount.ts +27 -0
- package/src/wallet/hooks/useStellarConnect.ts +60 -0
- package/src/wallet/hooks/useStellarDisconnect.ts +47 -0
- package/src/wallet/hooks/useUiKitConfig.ts +40 -0
- package/src/wallet/implementation/wallets-kit-implementation.ts +379 -0
- package/src/wallet/index.ts +11 -0
- package/src/wallet/services/__tests__/configResolutionService.test.ts +163 -0
- package/src/wallet/services/configResolutionService.ts +65 -0
- package/src/wallet/stellar-wallets-kit/StellarWalletsKitConnectButton.tsx +82 -0
- package/src/wallet/stellar-wallets-kit/__mocks__/@creit.tech/stellar-wallets-kit.ts +48 -0
- package/src/wallet/stellar-wallets-kit/__tests__/export-service.test.ts +93 -0
- package/src/wallet/stellar-wallets-kit/__tests__/stellarUiKitManager.test.ts +0 -0
- package/src/wallet/stellar-wallets-kit/config-generator.ts +75 -0
- package/src/wallet/stellar-wallets-kit/export-service.ts +19 -0
- package/src/wallet/stellar-wallets-kit/index.ts +3 -0
- package/src/wallet/stellar-wallets-kit/stellarUiKitManager.ts +235 -0
- package/src/wallet/types.ts +19 -0
- package/src/wallet/utils/__tests__/filterWalletComponents.test.ts +150 -0
- package/src/wallet/utils/__tests__/uiKitService.test.ts +189 -0
- package/src/wallet/utils/filterWalletComponents.ts +89 -0
- package/src/wallet/utils/index.ts +3 -0
- package/src/wallet/utils/stellarWalletImplementationManager.ts +118 -0
- package/src/wallet/utils/uiKitService.ts +74 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import type { StellarNetworkConfig } from '@openzeppelin/ui-types';
|
|
4
|
+
|
|
5
|
+
import { getStellarDefaultServiceConfig } from '../configuration/network-services';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Tests for getStellarDefaultServiceConfig function.
|
|
9
|
+
*
|
|
10
|
+
* Note: We test the pure function directly instead of instantiating StellarAdapter
|
|
11
|
+
* to avoid loading heavy Stellar SDK dependencies (WASM modules, etc.) which can
|
|
12
|
+
* cause memory issues during test execution.
|
|
13
|
+
*/
|
|
14
|
+
describe('getStellarDefaultServiceConfig', () => {
|
|
15
|
+
const createMockNetworkConfig = (
|
|
16
|
+
overrides: Partial<StellarNetworkConfig> = {}
|
|
17
|
+
): StellarNetworkConfig =>
|
|
18
|
+
({
|
|
19
|
+
id: 'stellar-testnet',
|
|
20
|
+
exportConstName: 'stellarTestnet',
|
|
21
|
+
name: 'Stellar Testnet',
|
|
22
|
+
ecosystem: 'stellar',
|
|
23
|
+
network: 'testnet',
|
|
24
|
+
type: 'testnet',
|
|
25
|
+
isTestnet: true,
|
|
26
|
+
sorobanRpcUrl: 'https://soroban-testnet.stellar.org',
|
|
27
|
+
networkPassphrase: 'Test SDF Network ; September 2015',
|
|
28
|
+
...overrides,
|
|
29
|
+
}) as StellarNetworkConfig;
|
|
30
|
+
|
|
31
|
+
describe('rpc service', () => {
|
|
32
|
+
it('should return RPC config when sorobanRpcUrl is present', () => {
|
|
33
|
+
const networkConfig = createMockNetworkConfig();
|
|
34
|
+
|
|
35
|
+
const result = getStellarDefaultServiceConfig(networkConfig, 'rpc');
|
|
36
|
+
|
|
37
|
+
expect(result).toEqual({
|
|
38
|
+
sorobanRpcUrl: 'https://soroban-testnet.stellar.org',
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should return null when sorobanRpcUrl is missing', () => {
|
|
43
|
+
const networkConfig = createMockNetworkConfig({
|
|
44
|
+
sorobanRpcUrl: undefined,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const result = getStellarDefaultServiceConfig(networkConfig, 'rpc');
|
|
48
|
+
|
|
49
|
+
expect(result).toBeNull();
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe('access-control-indexer service', () => {
|
|
54
|
+
it('should return indexer config when both URLs are present', () => {
|
|
55
|
+
const networkConfig = createMockNetworkConfig({
|
|
56
|
+
indexerUri: 'https://indexer.stellar.example/graphql',
|
|
57
|
+
indexerWsUri: 'wss://indexer.stellar.example/graphql',
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const result = getStellarDefaultServiceConfig(networkConfig, 'access-control-indexer');
|
|
61
|
+
|
|
62
|
+
expect(result).toEqual({
|
|
63
|
+
indexerUri: 'https://indexer.stellar.example/graphql',
|
|
64
|
+
indexerWsUri: 'wss://indexer.stellar.example/graphql',
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should return null when indexerUri is missing', () => {
|
|
69
|
+
const networkConfig = createMockNetworkConfig({
|
|
70
|
+
indexerWsUri: 'wss://indexer.stellar.example/graphql',
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const result = getStellarDefaultServiceConfig(networkConfig, 'access-control-indexer');
|
|
74
|
+
|
|
75
|
+
expect(result).toBeNull();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should return null when indexerWsUri is missing', () => {
|
|
79
|
+
const networkConfig = createMockNetworkConfig({
|
|
80
|
+
indexerUri: 'https://indexer.stellar.example/graphql',
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const result = getStellarDefaultServiceConfig(networkConfig, 'access-control-indexer');
|
|
84
|
+
|
|
85
|
+
expect(result).toBeNull();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should return null when neither indexer URL is present', () => {
|
|
89
|
+
const networkConfig = createMockNetworkConfig();
|
|
90
|
+
|
|
91
|
+
const result = getStellarDefaultServiceConfig(networkConfig, 'access-control-indexer');
|
|
92
|
+
|
|
93
|
+
expect(result).toBeNull();
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe('unknown service', () => {
|
|
98
|
+
it('should return null for unknown service IDs', () => {
|
|
99
|
+
const networkConfig = createMockNetworkConfig();
|
|
100
|
+
|
|
101
|
+
expect(getStellarDefaultServiceConfig(networkConfig, 'explorer')).toBeNull();
|
|
102
|
+
expect(getStellarDefaultServiceConfig(networkConfig, 'unknown')).toBeNull();
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
});
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Access Control Actions Module
|
|
3
|
+
*
|
|
4
|
+
* Assembles transaction data for access control operations (grant/revoke roles, transfer ownership)
|
|
5
|
+
* on Stellar (Soroban) contracts. These actions prepare transaction data that can be executed
|
|
6
|
+
* via the standard Stellar transaction execution flow.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { logger } from '@openzeppelin/ui-utils';
|
|
10
|
+
|
|
11
|
+
import type { StellarTransactionData } from '../transaction/formatter';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Special placeholder value that gets replaced with the connected wallet address
|
|
15
|
+
* at transaction execution time. Used when the caller parameter should be the
|
|
16
|
+
* transaction sender (connected wallet).
|
|
17
|
+
*
|
|
18
|
+
* @see EoaExecutionStrategy - replaces this placeholder before building the transaction
|
|
19
|
+
* @see RelayerExecutionStrategy - also replaces this placeholder before building the transaction
|
|
20
|
+
*/
|
|
21
|
+
export const CALLER_PLACEHOLDER = '__CALLER__';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Assembles transaction data for granting a role to an account
|
|
25
|
+
*
|
|
26
|
+
* Note: OpenZeppelin Stellar AccessControl (v0.5.x) requires a `caller` parameter for authorization.
|
|
27
|
+
* The caller is the address authorizing the role grant (must have admin privileges).
|
|
28
|
+
* Use CALLER_PLACEHOLDER to use the connected wallet address as the caller.
|
|
29
|
+
*
|
|
30
|
+
* @param contractAddress The contract address
|
|
31
|
+
* @param roleId The role identifier (Symbol)
|
|
32
|
+
* @param account The account address to grant the role to
|
|
33
|
+
* @param caller The address authorizing the grant (defaults to CALLER_PLACEHOLDER for connected wallet)
|
|
34
|
+
* @returns Transaction data ready for execution
|
|
35
|
+
*/
|
|
36
|
+
export function assembleGrantRoleAction(
|
|
37
|
+
contractAddress: string,
|
|
38
|
+
roleId: string,
|
|
39
|
+
account: string,
|
|
40
|
+
caller: string = CALLER_PLACEHOLDER
|
|
41
|
+
): StellarTransactionData {
|
|
42
|
+
logger.info(
|
|
43
|
+
'assembleGrantRoleAction',
|
|
44
|
+
`Assembling grant_role action for ${roleId} to ${account} (caller: ${caller})`
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
// Arguments for grant_role(caller: Address, account: Address, role: Symbol) - v0.5.x signature
|
|
48
|
+
// Note: args are raw values that will be converted to ScVal by the transaction execution flow
|
|
49
|
+
// The caller parameter is required by OpenZeppelin Stellar AccessControl for authorization
|
|
50
|
+
return {
|
|
51
|
+
contractAddress,
|
|
52
|
+
functionName: 'grant_role',
|
|
53
|
+
args: [caller, account, roleId],
|
|
54
|
+
argTypes: ['Address', 'Address', 'Symbol'],
|
|
55
|
+
argSchema: undefined,
|
|
56
|
+
transactionOptions: {},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Assembles transaction data for revoking a role from an account
|
|
62
|
+
*
|
|
63
|
+
* Note: OpenZeppelin Stellar AccessControl (v0.5.x) requires a `caller` parameter for authorization.
|
|
64
|
+
* The caller is the address authorizing the role revocation (must have admin privileges).
|
|
65
|
+
* Use CALLER_PLACEHOLDER to use the connected wallet address as the caller.
|
|
66
|
+
*
|
|
67
|
+
* @param contractAddress The contract address
|
|
68
|
+
* @param roleId The role identifier (Symbol)
|
|
69
|
+
* @param account The account address to revoke the role from
|
|
70
|
+
* @param caller The address authorizing the revocation (defaults to CALLER_PLACEHOLDER for connected wallet)
|
|
71
|
+
* @returns Transaction data ready for execution
|
|
72
|
+
*/
|
|
73
|
+
export function assembleRevokeRoleAction(
|
|
74
|
+
contractAddress: string,
|
|
75
|
+
roleId: string,
|
|
76
|
+
account: string,
|
|
77
|
+
caller: string = CALLER_PLACEHOLDER
|
|
78
|
+
): StellarTransactionData {
|
|
79
|
+
logger.info(
|
|
80
|
+
'assembleRevokeRoleAction',
|
|
81
|
+
`Assembling revoke_role action for ${roleId} from ${account} (caller: ${caller})`
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
// Arguments for revoke_role(caller: Address, account: Address, role: Symbol) - v0.5.x signature
|
|
85
|
+
// Note: args are raw values that will be converted to ScVal by the transaction execution flow
|
|
86
|
+
// The caller parameter is required by OpenZeppelin Stellar AccessControl for authorization
|
|
87
|
+
return {
|
|
88
|
+
contractAddress,
|
|
89
|
+
functionName: 'revoke_role',
|
|
90
|
+
args: [caller, account, roleId],
|
|
91
|
+
argTypes: ['Address', 'Address', 'Symbol'],
|
|
92
|
+
argSchema: undefined,
|
|
93
|
+
transactionOptions: {},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Assembles transaction data for transferring ownership of a contract
|
|
99
|
+
*
|
|
100
|
+
* For two-step Ownable contracts, this initiates a transfer that must be accepted
|
|
101
|
+
* by the pending owner before the expiration ledger.
|
|
102
|
+
*
|
|
103
|
+
* @param contractAddress The contract address
|
|
104
|
+
* @param newOwner The new owner address
|
|
105
|
+
* @param liveUntilLedger The ledger sequence by which the transfer must be accepted
|
|
106
|
+
* @returns Transaction data ready for execution
|
|
107
|
+
*/
|
|
108
|
+
export function assembleTransferOwnershipAction(
|
|
109
|
+
contractAddress: string,
|
|
110
|
+
newOwner: string,
|
|
111
|
+
liveUntilLedger: number
|
|
112
|
+
): StellarTransactionData {
|
|
113
|
+
logger.info(
|
|
114
|
+
'assembleTransferOwnershipAction',
|
|
115
|
+
`Assembling transfer_ownership action to ${newOwner} with expiration at ledger ${liveUntilLedger}`
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
// Arguments for transfer_ownership(new_owner: Address, live_until_ledger: u32)
|
|
119
|
+
// Note: args are raw values that will be converted to ScVal by the transaction execution flow
|
|
120
|
+
return {
|
|
121
|
+
contractAddress,
|
|
122
|
+
functionName: 'transfer_ownership',
|
|
123
|
+
args: [newOwner, liveUntilLedger],
|
|
124
|
+
argTypes: ['Address', 'u32'],
|
|
125
|
+
argSchema: undefined,
|
|
126
|
+
transactionOptions: {},
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Assembles transaction data for accepting a pending ownership transfer
|
|
132
|
+
*
|
|
133
|
+
* For two-step Ownable contracts, this completes a pending transfer initiated by
|
|
134
|
+
* the current owner. Must be called by the pending owner before the expiration ledger.
|
|
135
|
+
*
|
|
136
|
+
* @param contractAddress The contract address
|
|
137
|
+
* @returns Transaction data ready for execution
|
|
138
|
+
*/
|
|
139
|
+
export function assembleAcceptOwnershipAction(contractAddress: string): StellarTransactionData {
|
|
140
|
+
logger.info(
|
|
141
|
+
'assembleAcceptOwnershipAction',
|
|
142
|
+
`Assembling accept_ownership action for ${contractAddress}`
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
// accept_ownership() has no arguments - caller must be the pending owner
|
|
146
|
+
return {
|
|
147
|
+
contractAddress,
|
|
148
|
+
functionName: 'accept_ownership',
|
|
149
|
+
args: [],
|
|
150
|
+
argTypes: [],
|
|
151
|
+
argSchema: undefined,
|
|
152
|
+
transactionOptions: {},
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Assembles transaction data for initiating an admin role transfer
|
|
158
|
+
*
|
|
159
|
+
* For two-step AccessControl contracts, this initiates an admin transfer that must
|
|
160
|
+
* be accepted by the pending admin before the expiration ledger.
|
|
161
|
+
*
|
|
162
|
+
* @param contractAddress The contract address
|
|
163
|
+
* @param newAdmin The new admin address
|
|
164
|
+
* @param liveUntilLedger The ledger sequence by which the transfer must be accepted
|
|
165
|
+
* @returns Transaction data ready for execution
|
|
166
|
+
*/
|
|
167
|
+
export function assembleTransferAdminRoleAction(
|
|
168
|
+
contractAddress: string,
|
|
169
|
+
newAdmin: string,
|
|
170
|
+
liveUntilLedger: number
|
|
171
|
+
): StellarTransactionData {
|
|
172
|
+
logger.info(
|
|
173
|
+
'assembleTransferAdminRoleAction',
|
|
174
|
+
`Assembling transfer_admin_role action to ${newAdmin} with expiration at ledger ${liveUntilLedger}`
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
// Arguments for transfer_admin_role(new_admin: Address, live_until_ledger: u32)
|
|
178
|
+
// Note: args are raw values that will be converted to ScVal by the transaction execution flow
|
|
179
|
+
return {
|
|
180
|
+
contractAddress,
|
|
181
|
+
functionName: 'transfer_admin_role',
|
|
182
|
+
args: [newAdmin, liveUntilLedger],
|
|
183
|
+
argTypes: ['Address', 'u32'],
|
|
184
|
+
argSchema: undefined,
|
|
185
|
+
transactionOptions: {},
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Assembles transaction data for accepting a pending admin transfer
|
|
191
|
+
*
|
|
192
|
+
* For two-step AccessControl contracts, this completes a pending admin transfer
|
|
193
|
+
* initiated by the current admin. Must be called by the pending admin before the
|
|
194
|
+
* expiration ledger.
|
|
195
|
+
*
|
|
196
|
+
* @param contractAddress The contract address
|
|
197
|
+
* @returns Transaction data ready for execution
|
|
198
|
+
*/
|
|
199
|
+
export function assembleAcceptAdminTransferAction(contractAddress: string): StellarTransactionData {
|
|
200
|
+
logger.info(
|
|
201
|
+
'assembleAcceptAdminTransferAction',
|
|
202
|
+
`Assembling accept_admin_transfer action for ${contractAddress}`
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
// accept_admin_transfer() has no arguments - caller must be the pending admin
|
|
206
|
+
return {
|
|
207
|
+
contractAddress,
|
|
208
|
+
functionName: 'accept_admin_transfer',
|
|
209
|
+
args: [],
|
|
210
|
+
argTypes: [],
|
|
211
|
+
argSchema: undefined,
|
|
212
|
+
transactionOptions: {},
|
|
213
|
+
};
|
|
214
|
+
}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feature Detection Module
|
|
3
|
+
*
|
|
4
|
+
* Detects access control capabilities of Stellar (Soroban) contracts by analyzing
|
|
5
|
+
* their function interfaces. Supports detection of Ownable and AccessControl patterns
|
|
6
|
+
* from OpenZeppelin Stellar Contracts.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { AccessControlCapabilities, ContractSchema } from '@openzeppelin/ui-types';
|
|
10
|
+
import { UnsupportedContractFeatures } from '@openzeppelin/ui-types';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Known OpenZeppelin Ownable function signatures
|
|
14
|
+
*/
|
|
15
|
+
const OWNABLE_FUNCTIONS = {
|
|
16
|
+
required: ['get_owner'],
|
|
17
|
+
optional: ['transfer_ownership', 'accept_ownership', 'renounce_ownership'],
|
|
18
|
+
} as const;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Known OpenZeppelin AccessControl function signatures
|
|
22
|
+
*/
|
|
23
|
+
const ACCESS_CONTROL_FUNCTIONS = {
|
|
24
|
+
required: ['has_role', 'grant_role', 'revoke_role'],
|
|
25
|
+
optional: [
|
|
26
|
+
'get_role_admin',
|
|
27
|
+
'set_role_admin',
|
|
28
|
+
'get_admin',
|
|
29
|
+
'transfer_admin_role',
|
|
30
|
+
'accept_admin_transfer',
|
|
31
|
+
'renounce_admin',
|
|
32
|
+
'renounce_role',
|
|
33
|
+
],
|
|
34
|
+
} as const;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Functions that indicate role enumeration support
|
|
38
|
+
*/
|
|
39
|
+
const ENUMERATION_FUNCTIONS = ['get_role_member_count', 'get_role_member'] as const;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Detects access control capabilities of a Stellar contract
|
|
43
|
+
*
|
|
44
|
+
* @param contractSchema The contract schema to analyze
|
|
45
|
+
* @param indexerAvailable Whether an indexer is configured and available
|
|
46
|
+
* @returns Detected capabilities
|
|
47
|
+
*/
|
|
48
|
+
export function detectAccessControlCapabilities(
|
|
49
|
+
contractSchema: ContractSchema,
|
|
50
|
+
indexerAvailable = false
|
|
51
|
+
): AccessControlCapabilities {
|
|
52
|
+
const functionNames = new Set(contractSchema.functions.map((fn) => fn.name));
|
|
53
|
+
|
|
54
|
+
// Detect Ownable
|
|
55
|
+
const hasOwnable = OWNABLE_FUNCTIONS.required.every((fnName) => functionNames.has(fnName));
|
|
56
|
+
|
|
57
|
+
// Detect two-step Ownable (has accept_ownership function)
|
|
58
|
+
const hasTwoStepOwnable = hasOwnable && functionNames.has('accept_ownership');
|
|
59
|
+
|
|
60
|
+
// Detect AccessControl
|
|
61
|
+
const hasAccessControl = ACCESS_CONTROL_FUNCTIONS.required.every((fnName) =>
|
|
62
|
+
functionNames.has(fnName)
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
// Detect two-step admin transfer (has accept_admin_transfer function)
|
|
66
|
+
const hasTwoStepAdmin = hasAccessControl && functionNames.has('accept_admin_transfer');
|
|
67
|
+
|
|
68
|
+
// Detect enumerable roles
|
|
69
|
+
const hasEnumerableRoles = ENUMERATION_FUNCTIONS.every((fnName) => functionNames.has(fnName));
|
|
70
|
+
|
|
71
|
+
// History is only available when indexer is configured
|
|
72
|
+
const supportsHistory = indexerAvailable;
|
|
73
|
+
|
|
74
|
+
// Verify the contract against OZ interfaces
|
|
75
|
+
const verifiedAgainstOZInterfaces = verifyOZInterface(
|
|
76
|
+
functionNames,
|
|
77
|
+
hasOwnable,
|
|
78
|
+
hasAccessControl,
|
|
79
|
+
hasTwoStepOwnable,
|
|
80
|
+
hasTwoStepAdmin
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
// Collect notes about detected capabilities
|
|
84
|
+
const notes: string[] = [];
|
|
85
|
+
|
|
86
|
+
if (hasOwnable) {
|
|
87
|
+
if (hasTwoStepOwnable) {
|
|
88
|
+
notes.push('OpenZeppelin two-step Ownable interface detected (with accept_ownership)');
|
|
89
|
+
} else {
|
|
90
|
+
notes.push('OpenZeppelin Ownable interface detected');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (hasAccessControl) {
|
|
95
|
+
if (hasTwoStepAdmin) {
|
|
96
|
+
notes.push(
|
|
97
|
+
'OpenZeppelin two-step AccessControl interface detected (with accept_admin_transfer)'
|
|
98
|
+
);
|
|
99
|
+
} else {
|
|
100
|
+
notes.push('OpenZeppelin AccessControl interface detected');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (hasEnumerableRoles) {
|
|
105
|
+
notes.push('Role enumeration supported (get_role_member_count, get_role_member)');
|
|
106
|
+
} else if (hasAccessControl) {
|
|
107
|
+
notes.push('Role enumeration not available - requires event reconstruction');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!indexerAvailable && (hasOwnable || hasAccessControl)) {
|
|
111
|
+
notes.push('History queries unavailable without indexer configuration');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (!hasOwnable && !hasAccessControl) {
|
|
115
|
+
notes.push('No OpenZeppelin access control interfaces detected');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ── Stellar-specific capability flags ──────────────────────────────
|
|
119
|
+
// Stellar contracts have renounce_ownership but NOT renounce_role (uses revoke_role for self)
|
|
120
|
+
// Stellar has no cancel-admin-transfer or admin-delay-management concepts
|
|
121
|
+
const hasRenounceOwnership = hasOwnable && functionNames.has('renounce_ownership');
|
|
122
|
+
const hasRenounceRole = false; // Stellar uses revokeRole for self-revocation
|
|
123
|
+
const hasCancelAdminTransfer = false; // Not supported on Stellar
|
|
124
|
+
const hasAdminDelayManagement = false; // Not supported on Stellar
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
hasOwnable,
|
|
128
|
+
hasTwoStepOwnable,
|
|
129
|
+
hasAccessControl,
|
|
130
|
+
hasTwoStepAdmin,
|
|
131
|
+
hasEnumerableRoles,
|
|
132
|
+
supportsHistory,
|
|
133
|
+
verifiedAgainstOZInterfaces,
|
|
134
|
+
notes: notes.length > 0 ? notes : undefined,
|
|
135
|
+
hasRenounceOwnership,
|
|
136
|
+
hasRenounceRole,
|
|
137
|
+
hasCancelAdminTransfer,
|
|
138
|
+
hasAdminDelayManagement,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Verifies that the contract conforms to OpenZeppelin interfaces
|
|
144
|
+
*
|
|
145
|
+
* @param functionNames Set of function names in the contract
|
|
146
|
+
* @param hasOwnable Whether Ownable was detected
|
|
147
|
+
* @param hasAccessControl Whether AccessControl was detected
|
|
148
|
+
* @param hasTwoStepOwnable Whether two-step Ownable was detected
|
|
149
|
+
* @param hasTwoStepAdmin Whether two-step admin was detected
|
|
150
|
+
* @returns True if verified against OZ interfaces
|
|
151
|
+
*/
|
|
152
|
+
function verifyOZInterface(
|
|
153
|
+
functionNames: Set<string>,
|
|
154
|
+
hasOwnable: boolean,
|
|
155
|
+
hasAccessControl: boolean,
|
|
156
|
+
hasTwoStepOwnable = false,
|
|
157
|
+
hasTwoStepAdmin = false
|
|
158
|
+
): boolean {
|
|
159
|
+
// If no OZ patterns detected, not applicable
|
|
160
|
+
if (!hasOwnable && !hasAccessControl) {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Verify Ownable optional functions
|
|
165
|
+
// For two-step Ownable, require at least 3 of 4 optional functions
|
|
166
|
+
// For basic Ownable, at least 2 of 3 (excluding accept_ownership)
|
|
167
|
+
if (hasOwnable) {
|
|
168
|
+
const ownableOptionalCount = OWNABLE_FUNCTIONS.optional.filter((fnName) =>
|
|
169
|
+
functionNames.has(fnName)
|
|
170
|
+
).length;
|
|
171
|
+
|
|
172
|
+
if (hasTwoStepOwnable) {
|
|
173
|
+
// Two-step Ownable should have at least 3 of 4 optional functions
|
|
174
|
+
// (transfer_ownership, accept_ownership, renounce_ownership, and accept_ownership is guaranteed)
|
|
175
|
+
if (ownableOptionalCount < 3) {
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
} else {
|
|
179
|
+
// Basic Ownable should have at least 2 of 3 optional functions
|
|
180
|
+
if (ownableOptionalCount < 2) {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Verify AccessControl optional functions
|
|
187
|
+
// For two-step admin, require at least 5 of 7 optional functions
|
|
188
|
+
// For basic AccessControl, at least 4 of 7 should be present
|
|
189
|
+
if (hasAccessControl) {
|
|
190
|
+
const accessControlOptionalCount = ACCESS_CONTROL_FUNCTIONS.optional.filter((fnName) =>
|
|
191
|
+
functionNames.has(fnName)
|
|
192
|
+
).length;
|
|
193
|
+
|
|
194
|
+
if (hasTwoStepAdmin) {
|
|
195
|
+
// Two-step admin should have at least 5 of 7 optional functions
|
|
196
|
+
// (transfer_admin_role, accept_admin_transfer guaranteed, plus others)
|
|
197
|
+
if (accessControlOptionalCount < 5) {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
} else {
|
|
201
|
+
// Basic AccessControl should have at least 4 of 7 optional functions
|
|
202
|
+
if (accessControlOptionalCount < 4) {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Validates that a contract supports access control operations
|
|
213
|
+
*
|
|
214
|
+
* @param capabilities The detected capabilities
|
|
215
|
+
* @param contractAddress Optional contract address for error context
|
|
216
|
+
* @throws UnsupportedContractFeatures if contract doesn't support required operations
|
|
217
|
+
*/
|
|
218
|
+
export function validateAccessControlSupport(
|
|
219
|
+
capabilities: AccessControlCapabilities,
|
|
220
|
+
contractAddress?: string
|
|
221
|
+
): asserts capabilities is
|
|
222
|
+
| (AccessControlCapabilities & { hasOwnable: true })
|
|
223
|
+
| (AccessControlCapabilities & { hasAccessControl: true }) {
|
|
224
|
+
if (!capabilities.hasOwnable && !capabilities.hasAccessControl) {
|
|
225
|
+
throw new UnsupportedContractFeatures(
|
|
226
|
+
'Contract does not implement OpenZeppelin Ownable or AccessControl interfaces',
|
|
227
|
+
contractAddress,
|
|
228
|
+
['Ownable', 'AccessControl']
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (!capabilities.verifiedAgainstOZInterfaces) {
|
|
233
|
+
throw new UnsupportedContractFeatures(
|
|
234
|
+
'Contract interfaces do not conform to OpenZeppelin standards',
|
|
235
|
+
contractAddress
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Access Control Module
|
|
3
|
+
*
|
|
4
|
+
* Exports access control functionality for Stellar (Soroban) contracts including
|
|
5
|
+
* capability detection, on-chain data reading, action assembly, validation, indexer client,
|
|
6
|
+
* and the AccessControlService.
|
|
7
|
+
*
|
|
8
|
+
* ## Two-Step Ownable Support
|
|
9
|
+
*
|
|
10
|
+
* This module provides full support for OpenZeppelin's two-step Ownable pattern:
|
|
11
|
+
* - {@link StellarAccessControlService.getOwnership} - Returns ownership state (owned/pending/expired/renounced)
|
|
12
|
+
* - {@link StellarAccessControlService.transferOwnership} - Initiates two-step transfer with expiration
|
|
13
|
+
* - {@link StellarAccessControlService.acceptOwnership} - Accepts pending ownership transfer
|
|
14
|
+
* - {@link getCurrentLedger} - Gets current ledger sequence for expiration calculation
|
|
15
|
+
* - {@link validateExpirationLedger} - Validates expiration ledger before submission
|
|
16
|
+
*
|
|
17
|
+
* ## Two-Step Admin Transfer Support
|
|
18
|
+
*
|
|
19
|
+
* This module provides full support for OpenZeppelin's two-step admin transfer pattern:
|
|
20
|
+
* - {@link StellarAccessControlService.getAdminInfo} - Returns admin state (active/pending/expired/renounced)
|
|
21
|
+
* - {@link StellarAccessControlService.transferAdminRole} - Initiates two-step admin transfer with expiration
|
|
22
|
+
* - {@link StellarAccessControlService.acceptAdminTransfer} - Accepts pending admin transfer
|
|
23
|
+
* - {@link AdminTransferInitiatedEvent} - Pending admin transfer event from indexer
|
|
24
|
+
*
|
|
25
|
+
* ## Action Assembly
|
|
26
|
+
*
|
|
27
|
+
* - {@link assembleGrantRoleAction} - Prepares grant_role transaction
|
|
28
|
+
* - {@link assembleRevokeRoleAction} - Prepares revoke_role transaction
|
|
29
|
+
* - {@link assembleTransferOwnershipAction} - Prepares transfer_ownership transaction with expiration
|
|
30
|
+
* - {@link assembleAcceptOwnershipAction} - Prepares accept_ownership transaction
|
|
31
|
+
* - {@link assembleTransferAdminRoleAction} - Prepares transfer_admin_role transaction with expiration
|
|
32
|
+
* - {@link assembleAcceptAdminTransferAction} - Prepares accept_admin_transfer transaction
|
|
33
|
+
*
|
|
34
|
+
* ## Feature Detection
|
|
35
|
+
*
|
|
36
|
+
* - {@link detectAccessControlCapabilities} - Detects Ownable/AccessControl support
|
|
37
|
+
* - `hasTwoStepOwnable` capability flag indicates two-step ownership transfer support
|
|
38
|
+
* - `hasTwoStepAdmin` capability flag indicates two-step admin transfer support
|
|
39
|
+
*
|
|
40
|
+
* ## Indexer Client
|
|
41
|
+
*
|
|
42
|
+
* - {@link StellarIndexerClient} - Queries historical events and pending transfers
|
|
43
|
+
* - {@link OwnershipTransferStartedEvent} - Pending ownership transfer event from indexer
|
|
44
|
+
* - {@link AdminTransferInitiatedEvent} - Pending admin transfer event from indexer
|
|
45
|
+
*
|
|
46
|
+
* @module access-control
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
export * from './actions';
|
|
50
|
+
export * from './feature-detection';
|
|
51
|
+
export * from './indexer-client';
|
|
52
|
+
export * from './onchain-reader';
|
|
53
|
+
export * from './service';
|
|
54
|
+
export * from './validation';
|