@dethcrypto/eth-sdk
Version:
🛠Generate type-safe, lightweight SDK for your Ethereum smart contracts
68 lines • 3.44 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.NUMBER_OF_KNOWN_STORAGE_SLOTS = exports.ZEPPELIN_IMPLEMENTATION_STORAGE_SLOT = exports.EIP1967_IMPLEMENTATION_STORAGE_SLOT = exports.detectProxy = void 0;
const abi_1 = require("@ethersproject/abi");
const bignumber_1 = require("@ethersproject/bignumber");
const config_1 = require("../config");
async function detectProxy(address, abi, provider) {
const stored = await lookForImplementationAddr(address, abi, provider);
const asNumber = bignumber_1.BigNumber.from(stored || 0);
if (!asNumber.isZero()) {
const implAddress = asNumber.toHexString();
const code = await provider.getCode(implAddress);
const isContract = !bignumber_1.BigNumber.from(code).isZero();
if (isContract) {
return { implAddress: (0, config_1.parseAddress)(implAddress), isProxy: true };
}
}
return { isProxy: false };
}
exports.detectProxy = detectProxy;
async function lookForImplementationAddr(address, abi, provider) {
const call = async (name) => bignumber_1.BigNumber.from(await provider.call({
to: address,
data: new abi_1.Interface(abi).encodeFunctionData(name, []),
}));
// We check storage slot specified by EIP-1967 to hold implementation address.
// see https://eips.ethereum.org/EIPS/eip-1967
{
const stored = bignumber_1.BigNumber.from(await provider.getStorageAt(address, exports.EIP1967_IMPLEMENTATION_STORAGE_SLOT));
if (!stored.isZero())
return stored;
}
// We check storage slot specified by openzeppelin to hold implementation address.
// see https://github.com/OpenZeppelin/openzeppelin-labs/blob/master/initializer_with_sol_editing/contracts/UpgradeabilityProxy.sol#L24
{
const stored = bignumber_1.BigNumber.from(await provider.getStorageAt(address, exports.ZEPPELIN_IMPLEMENTATION_STORAGE_SLOT));
if (!stored.isZero())
return stored;
}
// If there is an `.implementation` getter, we try to call it.
const implementationGetter = abi.find((fragment) => fragment.name === 'implementation');
if (implementationGetter && isPossibleImplementationGetter(implementationGetter)) {
return call('implementation');
}
// Otherwise, we try shortest getter ending with "Implementation"
const possibleImplementationGetters = abi.filter(isPossibleImplementationGetter);
if (possibleImplementationGetters.length) {
const [frag] = possibleImplementationGetters.sort((a, b) => a.name.length - b.name.length);
return call(frag.name);
}
return null;
}
/** @internal */
exports.EIP1967_IMPLEMENTATION_STORAGE_SLOT = '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc';
exports.ZEPPELIN_IMPLEMENTATION_STORAGE_SLOT = '0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3';
exports.NUMBER_OF_KNOWN_STORAGE_SLOTS = 2;
const isPossibleImplementationGetter = (frag) => {
var _a;
if (frag.type === 'function' &&
frag.name &&
frag.name.match(/[iI]mplementation$/) &&
(frag.stateMutability === 'view' || frag.stateMutability === 'pure')) {
const output = (_a = frag.outputs) === null || _a === void 0 ? void 0 : _a[0];
return (output === null || output === void 0 ? void 0 : output.type) === 'address';
}
return false;
};
//# sourceMappingURL=detectProxy.js.map