Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 42 additions & 6 deletions src/contexts/ChainTransactionContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { isEqual, get } from 'lodash';
import { hash, num, shortString, uint256 } from 'starknet';
import { fetchBuildExecuteTransaction, fetchQuotes } from '@avnu/avnu-sdk';
import * as gasless from '@avnu/gasless-sdk';
import { ArgentSessionService, SessionDappService } from '@argent/x-sessions';

import useActivitiesContext from '~/hooks/useActivitiesContext';
import useCrewContext from '~/hooks/useCrewContext';
Expand All @@ -17,6 +18,7 @@ import api from '~/lib/api';
import { cleanseTxHash, safeBigInt } from '~/lib/utils';
import { TOKEN } from '~/lib/priceUtils';


const RETRY_INTERVAL = 5e3; // 5 seconds
const ChainTransactionContext = createContext();

Expand Down Expand Up @@ -405,12 +407,12 @@ export function ChainTransactionProvider({ children }) {
blockNumber,
blockTime,
chainId,
getOutsideExecutionData,
isDeployed,
logout,
payGasWithSwayIfPossible,
provider,
starknetSession,
starknetSessionData,
upgradeInsecureSession,
walletAccount
} = useSession();
Expand Down Expand Up @@ -461,6 +463,45 @@ export function ChainTransactionProvider({ children }) {
[blockNumber, crew?.Crew?.actionType, crew?.Crew?.actionRound, crew?._actionTypeTriggered]
);

// Retrieves an outside execution call and signs it
const getOutsideExecutionData = useCallback(async (calldata, gasTokenAddress, maxGasTokenAmount, useSessionKeyIfPossible) => {
const typedData = await gasless.fetchBuildTypedData(
accountAddress,
calldata,
gasTokenAddress,
maxGasTokenAmount,
{ baseUrl: process.env.REACT_APP_AVNU_API_URL }
);

let signature;

if (useSessionKeyIfPossible && starknetSessionData) {
const { accountSessionSignature, dappKey, sessionRequest } = starknetSessionData;
const beService = new ArgentSessionService(dappKey.publicKey, accountSessionSignature, process.env.REACT_APP_ARGENT_API);
const sessionDappService = new SessionDappService(
beService,
shortString.encodeShortString(chainId),
dappKey
);
const { Calldata: feeCalldata } = typedData.message.Calls[0];

// Add the fee call to the calldata
calldata.unshift({ contractAddress: gasTokenAddress, entrypoint: 'transfer', calldata: feeCalldata });
signature = await sessionDappService.getSessionSignatureForOutsideExecutionTypedData(
accountSessionSignature,
sessionRequest,
calldata,
accountAddress,
typedData,
false
);
} else {
signature = await walletAccount.signMessage(typedData);
}

return { typedData, signature };
}, [accountAddress, chainId, starknetSessionData, walletAccount]);

const executeWithAccount = useCallback(async (calls) => {
// Format calls for proper stringification
const formattedCalls = calls.map((call) => {
Expand Down Expand Up @@ -490,19 +531,14 @@ export function ChainTransactionProvider({ children }) {
[{ type: 'INVOKE_FUNCTION', payload: calls }],
{ skipValidate: true }
);
console.log('simulation', simulation);

const tokens = await gasless.fetchGasTokenPrices({ baseUrl: process.env.REACT_APP_AVNU_API_URL });
console.log('fetchGasTokenPrices', tokens);
gasToken = tokens.find((t) => Address.areEqual(t.tokenAddress, TOKEN.SWAY));
console.log('gasToken', gasToken);
console.log('swayBalance', swayBalance);

// Triple the fee estimation and check for sufficient funds to ensure transaction success
// TODO: figure out why some txs require this

maxFee = gasless.getGasFeesInGasToken(simulation[0].suggestedMaxFee, gasToken) * 3n;
console.log('maxFee', maxFee);

canPayGasWithSway = (swayBalance >= maxFee);
} catch (e) {
Expand Down
51 changes: 10 additions & 41 deletions src/contexts/SessionContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export function SessionProvider({ children }) {

const [connecting, setConnecting] = useState(false);
const [status, setStatus] = useState(STATUSES.DISCONNECTED);
const [starknetSessionData, setStarknetSessionData] = useState();
const [starknetSession, setStarknetSession] = useState();

const [connectedAccount, setConnectedAccount] = useState();
Expand Down Expand Up @@ -415,16 +416,20 @@ export function SessionProvider({ children }) {
}, [authenticate, currentSession, gameplay.useSessions]);

const createSessionAccount = useCallback(async () => {
const offchainSessionAccount = await buildSessionAccount({
const currentSessionData = {
accountSessionSignature: currentSession.sessionSignature,
dappKey: currentSession.sessionDappKey,
sessionRequest: currentSession.sessionRequest,
};
setStarknetSessionData(currentSessionData);

const offchainSessionAccount = await buildSessionAccount({
...currentSessionData,
provider,
chainId: resolveChainId(process.env.REACT_APP_CHAIN_ID, 'hex'),
address: currentSession.accountAddress,
dappKey: currentSession.sessionDappKey,
argentSessionServiceBaseUrl: process.env.REACT_APP_ARGENT_API
});

setStarknetSession(offchainSessionAccount);
}, [currentSession, provider]);

Expand Down Expand Up @@ -452,6 +457,7 @@ export function SessionProvider({ children }) {
}

setStarknetSession(null); // clear session key if it exists
setStarknetSessionData(null); // clear session key if it exists
} else if (status === STATUSES.CONNECTED) {
resumeOrAuthenticate().finally(() => {
setReadyForChildren(true);
Expand Down Expand Up @@ -503,43 +509,6 @@ export function SessionProvider({ children }) {
isFeeAbstractionCompatible
]);

// Retrieves an outside execution call and signs it
const getOutsideExecutionData = useCallback(async (calldata, gasTokenAddress, maxGasTokenAmount, canUseSessionKey) => {
let typedData = await gasless.fetchBuildTypedData(
currentSession.accountAddress,
calldata,
gasTokenAddress,
maxGasTokenAmount,
{ baseUrl: process.env.REACT_APP_AVNU_API_URL }
);

let signature;

if (canUseSessionKey && gameplay.useSessions && currentSession.sessionRequest) {
const dappKey = currentSession.sessionDappKey;
const sessionSignature = currentSession.sessionSignature;
const beService = new ArgentSessionService(dappKey.publicKey, sessionSignature, process.env.REACT_APP_ARGENT_API);
const chainId = shortString.encodeShortString(connectedChainId);
const sessionDappService = new SessionDappService(beService, chainId, dappKey);
const { Calldata: feeCalldata } = typedData.message.Calls[0];

// Add the fee call to the calldata
calldata.unshift({ contractAddress: gasTokenAddress, entrypoint: 'transfer', calldata: feeCalldata });
signature = await sessionDappService.getSessionSignatureForOutsideExecutionTypedData(
currentSession.sessionSignature,
currentSession.sessionRequest,
calldata,
currentSession.accountAddress,
typedData,
false
);
} else {
signature = await walletAccount.signMessage(typedData);
}

return { typedData, signature };
}, [currentSession, gameplay.useSessions, connectedChainId, connectedWalletId, walletAccount]);

// Block management -------------------------------------------------------------------------------------------------

// If using devnet, put "create block" on a timer since otherwise, blocks will not be advancing in the background
Expand Down Expand Up @@ -659,12 +628,12 @@ export function SessionProvider({ children }) {
authenticating: [STATUSES.AUTHENTICATING, STATUSES.CONNECTING].includes(status),
chainId: authenticated ? connectedChainId : null,
connecting: connecting || !!promptLogin,
getOutsideExecutionData,
isDeployed: authenticated ? currentSession?.isDeployed : null,
payGasWithSwayIfPossible: authenticated ? payGasWithSwayIfPossible : null,
provider,
shouldUseSessionKeys,
starknetSession,
starknetSessionData,
status,
token: authenticated ? currentSession?.token : null,
upgradeInsecureSession,
Expand Down
4 changes: 3 additions & 1 deletion src/hooks/useShoppingListData.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ const useShoppingListData = (asteroidId, lotId, productIds) => {
const [feeEnforcements, setFeeEnforcements] = useState();
const [feesLoading, setFeesLoading] = useState(true);
const loadFees = useCallback(async () => {
if (exchangesLoading) return;

const ids = (exchanges || []).map((e) => e.Control?.controller?.id);
if (ids?.length > 0) {
setFeesLoading(true);
Expand Down Expand Up @@ -49,7 +51,7 @@ const useShoppingListData = (asteroidId, lotId, productIds) => {
}
}
setFeesLoading(false);
}, [exchangesUpdatedAt]);
}, [exchangesLoading, exchangesUpdatedAt]);
useEffect(() => {
loadFees();
}, [loadFees]);
Expand Down